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.
Files changed (360) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +224 -19
  3. data/exe/emf +12 -0
  4. data/lib/emf/backend.rb +60 -0
  5. data/lib/emf/binary/class_field_accessors.rb +49 -0
  6. data/lib/emf/binary/float32_le.rb +54 -0
  7. data/lib/emf/binary/types.rb +40 -6
  8. data/lib/emf/conformance/context.rb +36 -0
  9. data/lib/emf/conformance/diagnostic.rb +18 -0
  10. data/lib/emf/conformance/manifest/emf_bitmap_misc.yml +622 -0
  11. data/lib/emf/conformance/manifest/emf_control_buffers.yml +61 -0
  12. data/lib/emf/conformance/manifest/emf_core.yml +117 -0
  13. data/lib/emf/conformance/manifest/emf_corpus_rules.yml +21 -0
  14. data/lib/emf/conformance/manifest/emf_objects_geometry.yml +229 -0
  15. data/lib/emf/conformance/manifest/emf_plus.yml +357 -0
  16. data/lib/emf/conformance/manifest/emf_plus_core.yml +33 -0
  17. data/lib/emf/conformance/manifest/emf_state_path.yml +403 -0
  18. data/lib/emf/conformance/manifest/emf_text.yml +97 -0
  19. data/lib/emf/conformance/manifest/wmf.yml +874 -0
  20. data/lib/emf/conformance/manifest.rb +79 -0
  21. data/lib/emf/conformance/profile.rb +35 -0
  22. data/lib/emf/conformance/record_types.rb +24 -0
  23. data/lib/emf/conformance/registry.rb +76 -0
  24. data/lib/emf/conformance/report.rb +83 -0
  25. data/lib/emf/conformance/rule.rb +16 -0
  26. data/lib/emf/conformance/rules/emf_bitmap.rb +357 -0
  27. data/lib/emf/conformance/rules/emf_comment_escape.rb +309 -0
  28. data/lib/emf/conformance/rules/emf_control_buffers.rb +137 -0
  29. data/lib/emf/conformance/rules/emf_corpus_named.rb +83 -0
  30. data/lib/emf/conformance/rules/emf_framing.rb +94 -0
  31. data/lib/emf/conformance/rules/emf_geometry.rb +282 -0
  32. data/lib/emf/conformance/rules/emf_header.rb +114 -0
  33. data/lib/emf/conformance/rules/emf_objects.rb +291 -0
  34. data/lib/emf/conformance/rules/emf_parse_recovery.rb +97 -0
  35. data/lib/emf/conformance/rules/emf_path_clip.rb +165 -0
  36. data/lib/emf/conformance/rules/emf_plus.rb +75 -0
  37. data/lib/emf/conformance/rules/emf_plus_common.rb +58 -0
  38. data/lib/emf/conformance/rules/emf_plus_drawing.rb +78 -0
  39. data/lib/emf/conformance/rules/emf_plus_effects.rb +104 -0
  40. data/lib/emf/conformance/rules/emf_plus_fields.rb +169 -0
  41. data/lib/emf/conformance/rules/emf_plus_fields_b.rb +102 -0
  42. data/lib/emf/conformance/rules/emf_plus_framing.rb +172 -0
  43. data/lib/emf/conformance/rules/emf_plus_objects.rb +97 -0
  44. data/lib/emf/conformance/rules/emf_plus_payloads.rb +320 -0
  45. data/lib/emf/conformance/rules/emf_plus_properties.rb +96 -0
  46. data/lib/emf/conformance/rules/emf_plus_state.rb +77 -0
  47. data/lib/emf/conformance/rules/emf_state.rb +244 -0
  48. data/lib/emf/conformance/rules/emf_text.rb +221 -0
  49. data/lib/emf/conformance/rules/emf_transform.rb +85 -0
  50. data/lib/emf/conformance/rules/wmf_buffers.rb +259 -0
  51. data/lib/emf/conformance/rules/wmf_framing.rb +146 -0
  52. data/lib/emf/conformance/rules/wmf_layout_records.rb +112 -0
  53. data/lib/emf/conformance/rules/wmf_objects.rb +168 -0
  54. data/lib/emf/conformance/rules/wmf_records.rb +191 -0
  55. data/lib/emf/conformance/rules/wmf_rejections.rb +184 -0
  56. data/lib/emf/conformance/runner.rb +61 -0
  57. data/lib/emf/conformance/ternary_raster_operations.rb +52 -0
  58. data/lib/emf/conformance.rb +31 -0
  59. data/lib/emf/emf_plus/effects/blur.rb +24 -0
  60. data/lib/emf/emf_plus/effects/brightness_contrast.rb +22 -0
  61. data/lib/emf/emf_plus/effects/color_balance.rb +24 -0
  62. data/lib/emf/emf_plus/effects/color_curve.rb +24 -0
  63. data/lib/emf/emf_plus/effects/color_lookup_table.rb +26 -0
  64. data/lib/emf/emf_plus/effects/color_matrix.rb +23 -0
  65. data/lib/emf/emf_plus/effects/effect.rb +50 -0
  66. data/lib/emf/emf_plus/effects/hue_saturation_lightness.rb +24 -0
  67. data/lib/emf/emf_plus/effects/levels.rb +24 -0
  68. data/lib/emf/emf_plus/effects/opaque.rb +19 -0
  69. data/lib/emf/emf_plus/effects/red_eye_correction.rb +35 -0
  70. data/lib/emf/emf_plus/effects/sharpen.rb +24 -0
  71. data/lib/emf/emf_plus/effects/tint.rb +22 -0
  72. data/lib/emf/emf_plus/effects.rb +45 -0
  73. data/lib/emf/emf_plus/frame.rb +41 -0
  74. data/lib/emf/emf_plus/links/effect_link.rb +16 -0
  75. data/lib/emf/emf_plus/links/get_dc_span.rb +18 -0
  76. data/lib/emf/emf_plus/links/link_error.rb +15 -0
  77. data/lib/emf/emf_plus/links.rb +77 -0
  78. data/lib/emf/emf_plus/object_chain.rb +108 -0
  79. data/lib/emf/emf_plus/object_table.rb +182 -0
  80. data/lib/emf/emf_plus/objects/brush.rb +296 -0
  81. data/lib/emf/emf_plus/objects/custom_line_cap.rb +106 -0
  82. data/lib/emf/emf_plus/objects/font.rb +47 -0
  83. data/lib/emf/emf_plus/objects/image.rb +68 -0
  84. data/lib/emf/emf_plus/objects/image_attributes.rb +28 -0
  85. data/lib/emf/emf_plus/objects/opaque.rb +19 -0
  86. data/lib/emf/emf_plus/objects/parts.rb +103 -0
  87. data/lib/emf/emf_plus/objects/path.rb +21 -0
  88. data/lib/emf/emf_plus/objects/pen.rb +132 -0
  89. data/lib/emf/emf_plus/objects/region.rb +39 -0
  90. data/lib/emf/emf_plus/objects/string_format.rb +55 -0
  91. data/lib/emf/emf_plus/objects/typed.rb +50 -0
  92. data/lib/emf/emf_plus/objects.rb +46 -0
  93. data/lib/emf/emf_plus/parser.rb +64 -2
  94. data/lib/emf/emf_plus/records/base.rb +107 -0
  95. data/lib/emf/emf_plus/records/begin_container.rb +31 -0
  96. data/lib/emf/emf_plus/records/begin_container_no_params.rb +20 -0
  97. data/lib/emf/emf_plus/records/body.rb +16 -0
  98. data/lib/emf/emf_plus/records/clear.rb +22 -0
  99. data/lib/emf/emf_plus/records/clip_transform_flags.rb +26 -0
  100. data/lib/emf/emf_plus/records/comment.rb +21 -0
  101. data/lib/emf/emf_plus/records/draw_arc.rb +33 -0
  102. data/lib/emf/emf_plus/records/draw_beziers.rb +31 -0
  103. data/lib/emf/emf_plus/records/draw_closed_curve.rb +34 -0
  104. data/lib/emf/emf_plus/records/draw_curve.rb +38 -0
  105. data/lib/emf/emf_plus/records/draw_driver_string.rb +61 -0
  106. data/lib/emf/emf_plus/records/draw_ellipse.rb +28 -0
  107. data/lib/emf/emf_plus/records/draw_image.rb +42 -0
  108. data/lib/emf/emf_plus/records/draw_image_points.rb +62 -0
  109. data/lib/emf/emf_plus/records/draw_lines.rb +34 -0
  110. data/lib/emf/emf_plus/records/draw_path.rb +23 -0
  111. data/lib/emf/emf_plus/records/draw_pie.rb +33 -0
  112. data/lib/emf/emf_plus/records/draw_rects.rb +30 -0
  113. data/lib/emf/emf_plus/records/draw_string.rb +41 -0
  114. data/lib/emf/emf_plus/records/end_container.rb +20 -0
  115. data/lib/emf/emf_plus/records/end_of_file.rb +17 -0
  116. data/lib/emf/emf_plus/records/fill_brush.rb +21 -0
  117. data/lib/emf/emf_plus/records/fill_closed_curve.rb +38 -0
  118. data/lib/emf/emf_plus/records/fill_ellipse.rb +29 -0
  119. data/lib/emf/emf_plus/records/fill_path.rb +23 -0
  120. data/lib/emf/emf_plus/records/fill_pie.rb +34 -0
  121. data/lib/emf/emf_plus/records/fill_polygon.rb +32 -0
  122. data/lib/emf/emf_plus/records/fill_rects.rb +31 -0
  123. data/lib/emf/emf_plus/records/fill_region.rb +23 -0
  124. data/lib/emf/emf_plus/records/get_dc.rb +18 -0
  125. data/lib/emf/emf_plus/records/header.rb +35 -0
  126. data/lib/emf/emf_plus/records/multiply_world_transform.rb +24 -0
  127. data/lib/emf/emf_plus/records/object.rb +32 -0
  128. data/lib/emf/emf_plus/records/offset_clip.rb +24 -0
  129. data/lib/emf/emf_plus/records/primitives/argb.rb +20 -0
  130. data/lib/emf/emf_plus/records/primitives/bitmap.rb +32 -0
  131. data/lib/emf/emf_plus/records/primitives/bitmap_data.rb +21 -0
  132. data/lib/emf/emf_plus/records/primitives/blend_colors.rb +22 -0
  133. data/lib/emf/emf_plus/records/primitives/blend_factors.rb +21 -0
  134. data/lib/emf/emf_plus/records/primitives/bounded_array.rb +38 -0
  135. data/lib/emf/emf_plus/records/primitives/byte_reader.rb +25 -0
  136. data/lib/emf/emf_plus/records/primitives/character_range.rb +20 -0
  137. data/lib/emf/emf_plus/records/primitives/compressed_image.rb +17 -0
  138. data/lib/emf/emf_plus/records/primitives/focus_scale_data.rb +20 -0
  139. data/lib/emf/emf_plus/records/primitives/graphics_version.rb +21 -0
  140. data/lib/emf/emf_plus/records/primitives/language_identifier.rb +22 -0
  141. data/lib/emf/emf_plus/records/primitives/metafile.rb +19 -0
  142. data/lib/emf/emf_plus/records/primitives/palette.rb +21 -0
  143. data/lib/emf/emf_plus/records/primitives/path.rb +89 -0
  144. data/lib/emf/emf_plus/records/primitives/path_point_type.rb +21 -0
  145. data/lib/emf/emf_plus/records/primitives/path_point_type_rle.rb +23 -0
  146. data/lib/emf/emf_plus/records/primitives/point.rb +18 -0
  147. data/lib/emf/emf_plus/records/primitives/point_array.rb +42 -0
  148. data/lib/emf/emf_plus/records/primitives/point_f.rb +19 -0
  149. data/lib/emf/emf_plus/records/primitives/point_r.rb +19 -0
  150. data/lib/emf/emf_plus/records/primitives/rect.rb +20 -0
  151. data/lib/emf/emf_plus/records/primitives/rect_array.rb +24 -0
  152. data/lib/emf/emf_plus/records/primitives/rect_f.rb +21 -0
  153. data/lib/emf/emf_plus/records/primitives/region_node.rb +68 -0
  154. data/lib/emf/emf_plus/records/primitives/region_node_child_nodes.rb +22 -0
  155. data/lib/emf/emf_plus/records/primitives/region_node_path.rb +37 -0
  156. data/lib/emf/emf_plus/records/primitives/relative_coordinate.rb +32 -0
  157. data/lib/emf/emf_plus/records/primitives/string_format_data.rb +23 -0
  158. data/lib/emf/emf_plus/records/primitives/transform_matrix.rb +23 -0
  159. data/lib/emf/emf_plus/records/primitives.rb +41 -0
  160. data/lib/emf/emf_plus/records/reset_clip.rb +17 -0
  161. data/lib/emf/emf_plus/records/reset_world_transform.rb +17 -0
  162. data/lib/emf/emf_plus/records/restore.rb +20 -0
  163. data/lib/emf/emf_plus/records/rotate_world_transform.rb +24 -0
  164. data/lib/emf/emf_plus/records/save.rb +20 -0
  165. data/lib/emf/emf_plus/records/scale_world_transform.rb +26 -0
  166. data/lib/emf/emf_plus/records/serializable_object.rb +32 -0
  167. data/lib/emf/emf_plus/records/set_anti_alias_mode.rb +25 -0
  168. data/lib/emf/emf_plus/records/set_clip_path.rb +20 -0
  169. data/lib/emf/emf_plus/records/set_clip_rect.rb +24 -0
  170. data/lib/emf/emf_plus/records/set_clip_region.rb +20 -0
  171. data/lib/emf/emf_plus/records/set_compositing_mode.rb +21 -0
  172. data/lib/emf/emf_plus/records/set_compositing_quality.rb +21 -0
  173. data/lib/emf/emf_plus/records/set_interpolation_mode.rb +21 -0
  174. data/lib/emf/emf_plus/records/set_page_transform.rb +25 -0
  175. data/lib/emf/emf_plus/records/set_pixel_offset_mode.rb +21 -0
  176. data/lib/emf/emf_plus/records/set_rendering_origin.rb +22 -0
  177. data/lib/emf/emf_plus/records/set_text_contrast.rb +24 -0
  178. data/lib/emf/emf_plus/records/set_text_rendering_hint.rb +21 -0
  179. data/lib/emf/emf_plus/records/set_ts_clip.rb +40 -0
  180. data/lib/emf/emf_plus/records/set_ts_graphics.rb +88 -0
  181. data/lib/emf/emf_plus/records/set_world_transform.rb +22 -0
  182. data/lib/emf/emf_plus/records/shape_flags.rb +23 -0
  183. data/lib/emf/emf_plus/records/translate_world_transform.rb +26 -0
  184. data/lib/emf/emf_plus/records/unknown.rb +20 -0
  185. data/lib/emf/emf_plus/records.rb +107 -0
  186. data/lib/emf/emf_plus/serializer.rb +9 -2
  187. data/lib/emf/emf_plus/stream/conformance.rb +68 -0
  188. data/lib/emf/emf_plus/stream/editor.rb +21 -0
  189. data/lib/emf/emf_plus/stream/emf_gap.rb +20 -0
  190. data/lib/emf/emf_plus/stream/link_index.rb +30 -0
  191. data/lib/emf/emf_plus/stream/rebuild.rb +47 -0
  192. data/lib/emf/emf_plus/stream/record_handle.rb +26 -0
  193. data/lib/emf/emf_plus/stream/segment.rb +70 -0
  194. data/lib/emf/emf_plus/stream.rb +153 -0
  195. data/lib/emf/emf_plus.rb +9 -0
  196. data/lib/emf/emr/binary/dib_header.rb +142 -0
  197. data/lib/emf/emr/binary/ext_text_out_fields.rb +43 -0
  198. data/lib/emf/emr/binary/extended_pen_buffers.rb +23 -0
  199. data/lib/emf/emr/binary/fast_fill.rb +1164 -0
  200. data/lib/emf/emr/binary/font.rb +159 -0
  201. data/lib/emf/emr/binary/lazy_utf16_array.rb +82 -0
  202. data/lib/emf/emr/binary/pack_plan.rb +144 -0
  203. data/lib/emf/emr/binary/pack_plan_check.rb +75 -0
  204. data/lib/emf/emr/binary/pack_plan_exact.rb +130 -0
  205. data/lib/emf/emr/binary/pack_plan_generate.rb +116 -0
  206. data/lib/emf/emr/binary/pack_plan_values.rb +92 -0
  207. data/lib/emf/emr/binary/palette_body.rb +19 -0
  208. data/lib/emf/emr/binary/palette_entry.rb +18 -0
  209. data/lib/emf/emr/binary/record.rb +33 -0
  210. data/lib/emf/emr/binary/records/alpha_blend.rb +43 -0
  211. data/lib/emf/emr/binary/records/angle_arc.rb +2 -2
  212. data/lib/emf/emr/binary/records/bit_blt.rb +14 -0
  213. data/lib/emf/emr/binary/records/color_correct_palette.rb +55 -1
  214. data/lib/emf/emr/binary/records/colormatch_to_target_w.rb +76 -1
  215. data/lib/emf/emr/binary/records/create_colorspace.rb +67 -1
  216. data/lib/emf/emr/binary/records/create_colorspace_w.rb +90 -1
  217. data/lib/emf/emr/binary/records/create_dib_pattern_brush_pt.rb +14 -0
  218. data/lib/emf/emr/binary/records/create_font_indirect_w.rb +6 -2
  219. data/lib/emf/emr/binary/records/create_mono_brush.rb +14 -0
  220. data/lib/emf/emr/binary/records/create_palette.rb +19 -1
  221. data/lib/emf/emr/binary/records/delete_colorspace.rb +22 -1
  222. data/lib/emf/emr/binary/records/draw_escape.rb +45 -1
  223. data/lib/emf/emr/binary/records/ellipse.rb +1 -1
  224. data/lib/emf/emr/binary/records/eof.rb +9 -0
  225. data/lib/emf/emr/binary/records/ext_create_pen.rb +80 -1
  226. data/lib/emf/emr/binary/records/ext_escape.rb +45 -1
  227. data/lib/emf/emr/binary/records/ext_flood_fill.rb +9 -2
  228. data/lib/emf/emr/binary/records/ext_select_clip_rgn.rb +9 -1
  229. data/lib/emf/emr/binary/records/ext_text_out_a.rb +16 -3
  230. data/lib/emf/emr/binary/records/ext_text_out_w.rb +14 -3
  231. data/lib/emf/emr/binary/records/fill_rgn.rb +11 -2
  232. data/lib/emf/emr/binary/records/force_ufi_mapping.rb +38 -1
  233. data/lib/emf/emr/binary/records/frame_rgn.rb +12 -2
  234. data/lib/emf/emr/binary/records/gls_bounded_record.rb +61 -1
  235. data/lib/emf/emr/binary/records/gls_record.rb +42 -1
  236. data/lib/emf/emr/binary/records/gradient_fill.rb +7 -0
  237. data/lib/emf/emr/binary/records/invert_rgn.rb +11 -3
  238. data/lib/emf/emr/binary/records/mask_blt.rb +66 -0
  239. data/lib/emf/emr/binary/records/named_escape.rb +78 -1
  240. data/lib/emf/emr/binary/records/paint_rgn.rb +11 -3
  241. data/lib/emf/emr/binary/records/pixel_format.rb +76 -1
  242. data/lib/emf/emr/binary/records/plg_blt.rb +66 -0
  243. data/lib/emf/emr/binary/records/poly_textout_a.rb +128 -1
  244. data/lib/emf/emr/binary/records/poly_textout_w.rb +128 -1
  245. data/lib/emf/emr/binary/records/raw.rb +3 -0
  246. data/lib/emf/emr/binary/records/realize_palette.rb +1 -0
  247. data/lib/emf/emr/binary/records/rectangle.rb +1 -1
  248. data/lib/emf/emr/binary/records/resize_palette.rb +16 -2
  249. data/lib/emf/emr/binary/records/round_rect.rb +2 -2
  250. data/lib/emf/emr/binary/records/select_palette.rb +1 -0
  251. data/lib/emf/emr/binary/records/set_color_adjustment.rb +136 -0
  252. data/lib/emf/emr/binary/records/set_colorspace.rb +22 -1
  253. data/lib/emf/emr/binary/records/set_dibits_to_device.rb +43 -0
  254. data/lib/emf/emr/binary/records/set_icm_profile_a.rb +71 -1
  255. data/lib/emf/emr/binary/records/set_icm_profile_w.rb +71 -1
  256. data/lib/emf/emr/binary/records/set_linked_ufis.rb +49 -1
  257. data/lib/emf/emr/binary/records/set_miter_limit.rb +1 -1
  258. data/lib/emf/emr/binary/records/set_palette_entries.rb +20 -2
  259. data/lib/emf/emr/binary/records/set_text_justification.rb +8 -2
  260. data/lib/emf/emr/binary/records/small_text_out.rb +55 -4
  261. data/lib/emf/emr/binary/records/stretch_blt.rb +14 -0
  262. data/lib/emf/emr/binary/records/stretch_dibits.rb +14 -0
  263. data/lib/emf/emr/binary/records/transparent_blt.rb +43 -0
  264. data/lib/emf/emr/binary/records.rb +4 -2
  265. data/lib/emf/emr/binary/region_data.rb +33 -0
  266. data/lib/emf/emr/binary/state_record_body.rb +33 -0
  267. data/lib/emf/emr/binary/thin_shell.rb +226 -0
  268. data/lib/emf/emr/binary/value_view/layout.rb +101 -0
  269. data/lib/emf/emr/binary/value_view/reader_source.rb +88 -0
  270. data/lib/emf/emr/binary/value_view.rb +173 -0
  271. data/lib/emf/emr/binary/variable_buffers.rb +87 -0
  272. data/lib/emf/emr/binary.rb +4 -0
  273. data/lib/emf/emr/filled_record.rb +77 -0
  274. data/lib/emf/emr/parser.rb +239 -50
  275. data/lib/emf/emr/serializer.rb +4 -5
  276. data/lib/emf/emr.rb +1 -0
  277. data/lib/emf/error.rb +24 -1
  278. data/lib/emf/model/diagnostic.rb +41 -0
  279. data/lib/emf/model/emr/header.rb +40 -1
  280. data/lib/emf/model/emr/records.rb +142 -6
  281. data/lib/emf/model/emr/wire_values.rb +43 -0
  282. data/lib/emf/model/emr.rb +1 -0
  283. data/lib/emf/model/geometry/color.rb +19 -1
  284. data/lib/emf/model/metafile.rb +22 -2
  285. data/lib/emf/model/wmf/bitmap_records.rb +155 -0
  286. data/lib/emf/model/wmf/brush_records.rb +45 -0
  287. data/lib/emf/model/wmf/dc_escape_records.rb +67 -0
  288. data/lib/emf/model/wmf/font_record.rb +40 -0
  289. data/lib/emf/model/wmf/geometry_records.rb +107 -0
  290. data/lib/emf/model/wmf/header.rb +36 -0
  291. data/lib/emf/model/wmf/object_records.rb +71 -0
  292. data/lib/emf/model/wmf/palette_records.rb +68 -0
  293. data/lib/emf/model/wmf/records.rb +247 -0
  294. data/lib/emf/model/wmf/region_records.rb +112 -0
  295. data/lib/emf/model/wmf/state_records.rb +188 -0
  296. data/lib/emf/model/wmf/text_records.rb +36 -0
  297. data/lib/emf/model/wmf.rb +11 -0
  298. data/lib/emf/model.rb +2 -0
  299. data/lib/emf/native/bitmap_fields.rb +58 -0
  300. data/lib/emf/native/diagnostic_contract.rb +39 -0
  301. data/lib/emf/native/diagnostics.rb +81 -0
  302. data/lib/emf/native/emf_plus_loader.rb +35 -0
  303. data/lib/emf/native/emf_plus_snapshot.rb +96 -0
  304. data/lib/emf/native/emf_plus_source.rb +20 -0
  305. data/lib/emf/native/emf_plus_stream.rb +150 -0
  306. data/lib/emf/native/fields.rb +369 -0
  307. data/lib/emf/native/font_fields.rb +87 -0
  308. data/lib/emf/native/gradient_fields.rb +23 -0
  309. data/lib/emf/native/header.rb +35 -0
  310. data/lib/emf/native/header_errors.rb +30 -0
  311. data/lib/emf/native/hydrator.rb +151 -0
  312. data/lib/emf/native/lazy_wire.rb +100 -0
  313. data/lib/emf/native/legacy_state_fields.rb +22 -0
  314. data/lib/emf/native/library_path.rb +68 -0
  315. data/lib/emf/native/loader.rb +120 -0
  316. data/lib/emf/native/palette_fields.rb +45 -0
  317. data/lib/emf/native/poly_text_fields.rb +116 -0
  318. data/lib/emf/native/resource_failure.rb +28 -0
  319. data/lib/emf/native/small_text_fields.rb +27 -0
  320. data/lib/emf/native/snapshot.rb +102 -0
  321. data/lib/emf/native/wmf_bitmap_fields.rb +58 -0
  322. data/lib/emf/native/wmf_brush_fields.rb +62 -0
  323. data/lib/emf/native/wmf_fields.rb +117 -0
  324. data/lib/emf/native/wmf_hydrator.rb +143 -0
  325. data/lib/emf/native/wmf_loading.rb +49 -0
  326. data/lib/emf/native/wmf_palette_fields.rb +60 -0
  327. data/lib/emf/native/wmf_region_fields.rb +92 -0
  328. data/lib/emf/native/wmf_rejection.rb +84 -0
  329. data/lib/emf/native/wmf_remaining_fields.rb +70 -0
  330. data/lib/emf/native/wmf_snapshot.rb +51 -0
  331. data/lib/emf/native/wmf_text_fields.rb +56 -0
  332. data/lib/emf/native.rb +61 -0
  333. data/lib/emf/parse_evidence.rb +57 -0
  334. data/lib/emf/resource_budget/bitmap.rb +66 -0
  335. data/lib/emf/resource_budget/gradient.rb +30 -0
  336. data/lib/emf/resource_budget/poly_text.rb +85 -0
  337. data/lib/emf/resource_budget.rb +382 -0
  338. data/lib/emf/resource_limits/emf_records.rb +130 -0
  339. data/lib/emf/resource_limits.rb +58 -0
  340. data/lib/emf/version.rb +1 -1
  341. data/lib/emf/visitors/dump.rb +4 -0
  342. data/lib/emf/visitors/stats.rb +6 -5
  343. data/lib/emf/wmf/binary.rb +150 -1
  344. data/lib/emf/wmf/bitmap_codec.rb +118 -0
  345. data/lib/emf/wmf/brush_codec.rb +102 -0
  346. data/lib/emf/wmf/dc_escape_codec.rb +75 -0
  347. data/lib/emf/wmf/dib.rb +187 -0
  348. data/lib/emf/wmf/embedded_emf.rb +137 -0
  349. data/lib/emf/wmf/escape_views.rb +89 -0
  350. data/lib/emf/wmf/evidence.rb +255 -0
  351. data/lib/emf/wmf/geometry_codec.rb +49 -0
  352. data/lib/emf/wmf/palette_codec.rb +85 -0
  353. data/lib/emf/wmf/parser.rb +147 -2
  354. data/lib/emf/wmf/recovery.rb +95 -0
  355. data/lib/emf/wmf/region_codec.rb +151 -0
  356. data/lib/emf/wmf/serializer.rb +164 -2
  357. data/lib/emf/wmf/text_codec.rb +123 -0
  358. data/lib/emf/wmf.rb +7 -0
  359. data/lib/emf.rb +13 -9
  360. metadata +287 -10
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ # Conformance lens: checks a parsed metafile against the Microsoft specs and
5
+ # reports structured diagnostics in svg_conform's report shape.
6
+ #
7
+ # Emf::Conformance.check("file.emf", profile: :tolerant).to_h
8
+ #
9
+ # Rules read only the public model API (records, wire fields, Metafile#errors,
10
+ # Metafile#emf_plus_stream); they never touch a backend-specific class.
11
+ module Conformance
12
+ autoload :Diagnostic, "emf/conformance/diagnostic"
13
+ autoload :Report, "emf/conformance/report"
14
+ autoload :Profile, "emf/conformance/profile"
15
+ autoload :Rule, "emf/conformance/rule"
16
+ autoload :Registry, "emf/conformance/registry"
17
+ autoload :Context, "emf/conformance/context"
18
+ autoload :Runner, "emf/conformance/runner"
19
+ autoload :RecordTypes, "emf/conformance/record_types"
20
+ autoload :Manifest, "emf/conformance/manifest"
21
+ autoload :TernaryRasterOperations, "emf/conformance/ternary_raster_operations"
22
+
23
+ # @param input [String, Pathname, Emf::Model::Metafile] a file path, raw bytes or a parsed metafile
24
+ # @param profile [Symbol] :strict (spec-conformant only) or :tolerant (accepts the real-world corpus)
25
+ # @param registry [Registry] rule set; defaults to every rule file under conformance/rules
26
+ # @return [Report]
27
+ def self.check(input, profile: :strict, registry: Registry.default, now: Time.now)
28
+ Runner.new(Profile.fetch(profile), registry).call(input, now: now)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Effects
8
+ # BlurEffect (2.2.3.1).
9
+ class Blur < Effect
10
+ GUID = "633c80a4-1843-482b-9ef2-be2834c5fdd4"
11
+
12
+ class BlurBody < Records::Body
13
+ endian :little
14
+ float32_le :blur_radius
15
+ uint32 :expand_edge
16
+ end
17
+ BODY = BlurBody
18
+
19
+ def blur_radius = body.blur_radius
20
+ def expand_edge = body.expand_edge
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # BrightnessContrastEffect (2.2.3.2).
7
+ class BrightnessContrast < Effect
8
+ GUID = "d3a1dbe1-8ec4-4c17-9f4c-ea97ad1c343d"
9
+
10
+ class BrightnessContrastBody < Records::Body
11
+ endian :little
12
+ int32 :brightness_level
13
+ int32 :contrast_level
14
+ end
15
+ BODY = BrightnessContrastBody
16
+
17
+ def brightness_level = body.brightness_level
18
+ def contrast_level = body.contrast_level
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # ColorBalanceEffect (2.2.3.3).
7
+ class ColorBalance < Effect
8
+ GUID = "537e597d-251e-48da-9664-29ca496b70f8"
9
+
10
+ class ColorBalanceBody < Records::Body
11
+ endian :little
12
+ int32 :cyan_red
13
+ int32 :magenta_green
14
+ int32 :yellow_blue
15
+ end
16
+ BODY = ColorBalanceBody
17
+
18
+ def cyan_red = body.cyan_red
19
+ def magenta_green = body.magenta_green
20
+ def yellow_blue = body.yellow_blue
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # ColorCurveEffect (2.2.3.4).
7
+ class ColorCurve < Effect
8
+ GUID = "dd6a0022-58e4-4a67-9d9b-d48eb881a53d"
9
+
10
+ class ColorCurveBody < Records::Body
11
+ endian :little
12
+ uint32 :curve_adjustment
13
+ uint32 :curve_channel
14
+ int32 :adjustment_intensity
15
+ end
16
+ BODY = ColorCurveBody
17
+
18
+ def curve_adjustment = body.curve_adjustment
19
+ def curve_channel = body.curve_channel
20
+ def adjustment_intensity = body.adjustment_intensity
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # ColorLookupTableEffect (2.2.3.5).
7
+ class ColorLookupTable < Effect
8
+ GUID = "a7ce72a9-0f7f-40d7-b3cc-d0c02d5c3212"
9
+
10
+ class ColorLookupTableBody < Records::Body
11
+ endian :little
12
+ string :blue_lookup_table, length: 256
13
+ string :green_lookup_table, length: 256
14
+ string :red_lookup_table, length: 256
15
+ string :alpha_lookup_table, length: 256
16
+ end
17
+ BODY = ColorLookupTableBody
18
+
19
+ def blue_lookup_table = body.blue_lookup_table.to_s
20
+ def green_lookup_table = body.green_lookup_table.to_s
21
+ def red_lookup_table = body.red_lookup_table.to_s
22
+ def alpha_lookup_table = body.alpha_lookup_table.to_s
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Effects
8
+ # ColorMatrixEffect (2.2.3.6).
9
+ class ColorMatrix < Effect
10
+ GUID = "718f2615-7933-40e3-a511-5f68fe14dd74"
11
+
12
+ class ColorMatrixBody < Records::Body
13
+ endian :little
14
+ array :matrix_values, type: :float32_le, initial_length: 25
15
+ end
16
+ BODY = ColorMatrixBody
17
+
18
+ # Matrix[row][column]; the spec lists each column's five rows in turn.
19
+ def matrix = Array.new(5) { |row| Array.new(5) { |column| body.matrix_values[(column * 5) + row] } }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bindata"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Effects
8
+ # An image effect parameter block read through a BinData class (BODY in
9
+ # the subclass). Bytes after the body stay in `trailing`, so an unedited
10
+ # effect re-encodes to its buffer.
11
+ class Effect
12
+ attr_reader :body, :trailing
13
+
14
+ def initialize(buffer, offset:, record_code:)
15
+ @buffer = buffer
16
+ @offset = offset
17
+ @record_code = record_code
18
+ check_bounds
19
+ @body = read_body
20
+ @trailing = buffer.byteslice(@body.num_bytes..).freeze
21
+ end
22
+
23
+ def to_binary = body.to_binary_s + trailing
24
+
25
+ private
26
+
27
+ attr_reader :buffer
28
+
29
+ def check_bounds; end
30
+
31
+ def read_body
32
+ self.class::BODY.new.read(buffer)
33
+ rescue IOError, BinData::ValidityError => e
34
+ fail_parse(e.message)
35
+ end
36
+
37
+ def fail_parse(message)
38
+ raise ParseError.new(offset: @offset, record_code: @record_code,
39
+ message: "malformed #{self.class.name.split('::').last} effect at offset #{@offset}: #{message}")
40
+ end
41
+
42
+ def require_room(needed, what)
43
+ return if needed <= buffer.bytesize
44
+
45
+ fail_parse("#{what} needs #{needed} bytes, buffer holds #{buffer.bytesize}")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # HueSaturationLightnessEffect (2.2.3.7).
7
+ class HueSaturationLightness < Effect
8
+ GUID = "8b2dd6c3-eb07-4d87-a5f0-7108e26a9c5f"
9
+
10
+ class HueSaturationLightnessBody < Records::Body
11
+ endian :little
12
+ int32 :hue_level
13
+ int32 :saturation_level
14
+ int32 :lightness_level
15
+ end
16
+ BODY = HueSaturationLightnessBody
17
+
18
+ def hue_level = body.hue_level
19
+ def saturation_level = body.saturation_level
20
+ def lightness_level = body.lightness_level
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # LevelsEffect (2.2.3.8).
7
+ class Levels < Effect
8
+ GUID = "99c354ec-2a31-4f3a-8c34-17a803b33a25"
9
+
10
+ class LevelsBody < Records::Body
11
+ endian :little
12
+ int32 :highlight
13
+ int32 :mid_tone
14
+ int32 :shadow
15
+ end
16
+ BODY = LevelsBody
17
+
18
+ def highlight = body.highlight
19
+ def mid_tone = body.mid_tone
20
+ def shadow = body.shadow
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # A parameter block of an unknown GUID, or a ColorCurve block that does
7
+ # not fit its layout (Windows writes those corrupt, ch05 note 10).
8
+ class Opaque
9
+ attr_reader :buffer
10
+
11
+ def initialize(buffer)
12
+ @buffer = buffer
13
+ end
14
+
15
+ def to_binary = buffer
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # RedEyeCorrectionEffect (2.2.3.9): NumberOfAreas WMF RectL objects, read with
7
+ # the core Emf::Binary::Types::RectL (a nested RectL would replace it in
8
+ # BinData's global registry).
9
+ class RedEyeCorrection < Effect
10
+ GUID = "74d29d05-69a4-4266-9549-3cc52836b632"
11
+ RECT_SIZE = 16
12
+
13
+ class RedEyeCorrectionBody < Records::Body
14
+ endian :little
15
+ int32 :number_of_areas
16
+ array :areas, type: :rect_l, initial_length: :number_of_areas
17
+ end
18
+ BODY = RedEyeCorrectionBody
19
+
20
+ def number_of_areas = body.number_of_areas
21
+ def areas = body.areas.map { |r| [r.left, r.top, r.right, r.bottom] }
22
+
23
+ private
24
+
25
+ def check_bounds
26
+ return if buffer.bytesize < 4
27
+
28
+ count = buffer.unpack1("l<")
29
+ fail_parse("negative NumberOfAreas #{count}") if count.negative?
30
+ require_room(4 + (count * RECT_SIZE), "Areas of #{count} rectangles")
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Effects
8
+ # SharpenEffect (2.2.3.10).
9
+ class Sharpen < Effect
10
+ GUID = "63cbf3ee-c526-402c-8f71-62c540bf5142"
11
+
12
+ class SharpenBody < Records::Body
13
+ endian :little
14
+ float32_le :radius
15
+ float32_le :amount
16
+ end
17
+ BODY = SharpenBody
18
+
19
+ def radius = body.radius
20
+ def amount = body.amount
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Effects
6
+ # TintEffect (2.2.3.11).
7
+ class Tint < Effect
8
+ GUID = "1077af00-2848-4441-9489-44ad4c2d7a2c"
9
+
10
+ class TintBody < Records::Body
11
+ endian :little
12
+ int32 :hue
13
+ int32 :amount
14
+ end
15
+ BODY = TintBody
16
+
17
+ def hue = body.hue
18
+ def amount = body.amount
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ # Typed image effect parameter blocks (2.2.3), identified by the GUID of
6
+ # the EmfPlusSerializableObject that carries them.
7
+ module Effects
8
+ autoload :Effect, "emf/emf_plus/effects/effect"
9
+ autoload :Opaque, "emf/emf_plus/effects/opaque"
10
+ autoload :Blur, "emf/emf_plus/effects/blur"
11
+ autoload :BrightnessContrast, "emf/emf_plus/effects/brightness_contrast"
12
+ autoload :ColorBalance, "emf/emf_plus/effects/color_balance"
13
+ autoload :ColorCurve, "emf/emf_plus/effects/color_curve"
14
+ autoload :ColorLookupTable, "emf/emf_plus/effects/color_lookup_table"
15
+ autoload :ColorMatrix, "emf/emf_plus/effects/color_matrix"
16
+ autoload :HueSaturationLightness, "emf/emf_plus/effects/hue_saturation_lightness"
17
+ autoload :Levels, "emf/emf_plus/effects/levels"
18
+ autoload :RedEyeCorrection, "emf/emf_plus/effects/red_eye_correction"
19
+ autoload :Sharpen, "emf/emf_plus/effects/sharpen"
20
+ autoload :Tint, "emf/emf_plus/effects/tint"
21
+
22
+ CLASSES = %i[Blur BrightnessContrast ColorBalance ColorCurve ColorLookupTable ColorMatrix
23
+ HueSaturationLightness Levels RedEyeCorrection Sharpen Tint].freeze
24
+ private_constant :CLASSES
25
+
26
+ module_function
27
+
28
+ # `serializable` is a Records::SerializableObject.
29
+ def decode(serializable)
30
+ klass = lookup(serializable.object_guid)
31
+ return Opaque.new(serializable.buffer) unless klass
32
+
33
+ klass.new(serializable.buffer, offset: serializable.offset, record_code: serializable.type)
34
+ rescue ParseError
35
+ raise unless klass == ColorCurve
36
+
37
+ Opaque.new(serializable.buffer)
38
+ end
39
+
40
+ def lookup(guid)
41
+ CLASSES.map { |name| const_get(name) }.find { |klass| guid == klass::GUID }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ # One EMF+ record as framed on the wire: the 12-byte header fields plus the
6
+ # undecoded body. `data` is every byte after the header up to `size`, so a
7
+ # frame always re-serializes to the bytes it was read from.
8
+ #
9
+ # `offset` is the frame's position within its byte stream; `comment_index`
10
+ # is the index into Metafile#records of the EMR_COMMENT it was read from
11
+ # (nil when framed from a bare stream).
12
+ class Frame
13
+ HEADER_BYTES = 12
14
+
15
+ attr_reader :type, :flags, :size, :data_size, :data, :offset, :comment_index
16
+
17
+ def initialize(type:, flags:, size:, data_size:, data:, offset: 0, comment_index: nil)
18
+ @type = type
19
+ @flags = flags
20
+ @size = size
21
+ @data_size = data_size
22
+ @data = data.b.freeze
23
+ @offset = offset
24
+ @comment_index = comment_index
25
+ freeze
26
+ end
27
+
28
+ # Size must stay 32-bit aligned, so the body is zero-padded; DataSize keeps the real length.
29
+ def with_data(new_data)
30
+ padded = new_data.b + ("\0" * (-new_data.bytesize % 4))
31
+ self.class.new(type: type, flags: flags, size: HEADER_BYTES + padded.bytesize,
32
+ data_size: new_data.bytesize, data: padded,
33
+ offset: offset, comment_index: comment_index)
34
+ end
35
+
36
+ def to_binary
37
+ [type, flags, size, data_size].pack("vvVV") + data
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Links
6
+ # An E-flag EmfPlusDrawImagePoints frame paired with the
7
+ # EmfPlusSerializableObject frame that supplies its effect.
8
+ EffectLink = Struct.new(:draw, :effect, keyword_init: true) do
9
+ def initialize(**)
10
+ super
11
+ freeze
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Links
6
+ # The EMF records a single EmfPlusGetDC makes drawable. `emr_range` indexes
7
+ # Metafile#records and is empty when another EMF+ record follows the GetDC.
8
+ GetDcSpan = Struct.new(:comment_index, :offset, :emr_range, keyword_init: true) do
9
+ def initialize(**)
10
+ super
11
+ freeze
12
+ end
13
+
14
+ def empty? = emr_range.none?
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Links
6
+ # A linking diagnostic. `reason` is :missing_effect or :non_adjacent_effect.
7
+ LinkError = Struct.new(:reason, :comment_index, :offset, :message, keyword_init: true) do
8
+ def initialize(**)
9
+ super
10
+ freeze
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ # Ordering and linking rules that span EMF+ records and the EMF records
6
+ # around them. Works on framed records (Parser.from_metafile), never decodes
7
+ # or alters bytes, and reports problems instead of raising.
8
+ module Links
9
+ autoload :GetDcSpan, "emf/emf_plus/links/get_dc_span"
10
+ autoload :EffectLink, "emf/emf_plus/links/effect_link"
11
+ autoload :LinkError, "emf/emf_plus/links/link_error"
12
+
13
+ GET_DC = 0x4004
14
+ SERIALIZABLE_OBJECT = 0x4038
15
+ DRAW_IMAGE_POINTS = 0x401B
16
+ EFFECT_FLAG = 0x2000
17
+
18
+ module_function
19
+
20
+ # MS-EMFPLUS 2.3.3.2: EMF records are processed after an EmfPlusGetDC
21
+ # until the next EMF+ record. A GetDC that is not last in its comment is
22
+ # followed by an EMF+ record at once, so its span is empty; otherwise the
23
+ # span runs to the next comment carrying EMF+ records (or the end).
24
+ def get_dc_spans(metafile, frames)
25
+ frames.each_with_index.filter_map do |frame, index|
26
+ next unless frame.type == GET_DC
27
+
28
+ GetDcSpan.new(comment_index: frame.comment_index, offset: frame.offset,
29
+ emr_range: emr_range_after(frames, index, metafile.records.size))
30
+ end
31
+ end
32
+
33
+ # MS-EMFPLUS 2.3.4.9 says an E-flag DrawImagePoints needs an "earlier"
34
+ # SerializableObject without saying how far back. Only the immediately
35
+ # preceding EMF+ record (across comments) is linked; anything else is
36
+ # reported. Returns [links, errors].
37
+ def effect_links(frames)
38
+ links = []
39
+ errors = []
40
+ frames.each_with_index do |frame, index|
41
+ next unless effect_draw?(frame)
42
+
43
+ effect = frames[index - 1] if index.positive? && frames[index - 1].type == SERIALIZABLE_OBJECT
44
+ effect ? links << EffectLink.new(draw: frame, effect: effect) : errors << effect_error(frames, index)
45
+ end
46
+ [links.freeze, errors.freeze]
47
+ end
48
+
49
+ def emr_range_after(frames, index, record_count)
50
+ frame = frames[index]
51
+ following = frames[index + 1]
52
+ first = frame.comment_index + 1
53
+ return first...first if following&.comment_index == frame.comment_index
54
+
55
+ first...(following ? following.comment_index : record_count)
56
+ end
57
+
58
+ def effect_draw?(frame)
59
+ frame.type == DRAW_IMAGE_POINTS && frame.flags.anybits?(EFFECT_FLAG)
60
+ end
61
+
62
+ def effect_error(frames, index)
63
+ frame = frames[index]
64
+ earlier = frames[0...index].any? { |f| f.type == SERIALIZABLE_OBJECT }
65
+ reason = earlier ? :non_adjacent_effect : :missing_effect
66
+ message = if earlier
67
+ "E-flag DrawImagePoints is not immediately preceded by a SerializableObject"
68
+ else
69
+ "E-flag DrawImagePoints has no SerializableObject"
70
+ end
71
+ LinkError.new(reason: reason, comment_index: frame.comment_index, offset: frame.offset, message: message)
72
+ end
73
+
74
+ private_class_method :emr_range_after, :effect_draw?, :effect_error
75
+ end
76
+ end
77
+ end