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
@@ -2,11 +2,73 @@
2
2
 
3
3
  module Emf
4
4
  module EmfPlus
5
+ # Frames EMF+ records without decoding their bodies. Unknown record types
6
+ # are framed like any other, so nothing is dropped.
7
+ #
8
+ # Malformed framing raises Emf::ParseError. Pass `errors:` (an Array) to
9
+ # instead collect the ParseError and stop framing the stream at that point,
10
+ # the way Emr::Parser treats a corrupt EMR record.
5
11
  module Parser
6
12
  module_function
7
13
 
8
- def call(_bytes)
9
- raise Error, "EMF+ parser not yet implemented — see TODO.impl/14-emfplus-parser.md"
14
+ def call(bytes, errors: nil, comment_index: nil)
15
+ bytes = bytes.b
16
+ frames = []
17
+ offset = 0
18
+ while offset < bytes.bytesize
19
+ frame = read_frame(bytes, offset, comment_index)
20
+ frames << frame
21
+ offset += frame.size
22
+ end
23
+ frames
24
+ rescue ParseError => e
25
+ raise unless errors
26
+
27
+ errors << e
28
+ frames
29
+ end
30
+
31
+ # Frames each EMF+ comment of a parsed Metafile separately, tagging every
32
+ # frame with the index (into metafile.records) of the comment it came
33
+ # from. Concatenating the comment payloads gives Metafile#emf_plus.
34
+ def from_metafile(metafile, errors: nil)
35
+ metafile.records.each_with_index.flat_map do |record, index|
36
+ next [] unless record.wire.is_a?(Emf::Emr::Binary::Records::Comment)
37
+
38
+ call(Emf::Emr::Parser.extract_emf_plus(record.wire), errors: errors, comment_index: index)
39
+ end
40
+ end
41
+
42
+ def read_frame(bytes, offset, comment_index)
43
+ remaining = bytes.bytesize - offset
44
+ fail_at(offset, nil, "truncated EMF+ record header (#{remaining} bytes left)") if remaining < Frame::HEADER_BYTES
45
+
46
+ type, flags, size, data_size = bytes.byteslice(offset, Frame::HEADER_BYTES).unpack("vvVV")
47
+ validate_sizes(offset, type, size, data_size, remaining, comment_index)
48
+ Frame.new(type: type, flags: flags, size: size, data_size: data_size,
49
+ data: bytes.byteslice(offset + Frame::HEADER_BYTES, size - Frame::HEADER_BYTES),
50
+ offset: offset, comment_index: comment_index)
51
+ end
52
+
53
+ def validate_sizes(offset, type, size, data_size, remaining, comment_index = nil)
54
+ fail_at(offset, type, "EMF+ record Size #{size} < #{Frame::HEADER_BYTES}") if size < Frame::HEADER_BYTES
55
+ fail_at(offset, type, "EMF+ record Size #{size} is not a multiple of 4") unless (size % 4).zero?
56
+ fail_at(offset, type, "EMF+ record DataSize #{data_size} exceeds Size #{size} - 12") if data_size > size - Frame::HEADER_BYTES
57
+ fail_at(offset, type, truncated_message(size, remaining, comment_index, offset)) if size > remaining
58
+ end
59
+
60
+ # MS-EMFPLUS 1.3 and MS-EMF 2.3.3.2: an EMR_COMMENT_EMFPLUS holds whole
61
+ # EMF+ records; only an EmfPlusObject (C flag) continues in a later record.
62
+ # A record that runs past its comment is therefore malformed, not split.
63
+ def truncated_message(size, remaining, comment_index, offset)
64
+ return "truncated EMF+ record (Size #{size}, #{remaining} bytes left)" unless comment_index
65
+
66
+ "EMF+ record (Size #{size}) overruns EMR_COMMENT_EMFPLUS record #{comment_index} by " \
67
+ "#{size - remaining} bytes; a record cannot span comments (comment payload offset #{offset})"
68
+ end
69
+
70
+ def fail_at(offset, type, message)
71
+ raise ParseError.new(offset: offset, record_code: type, message: "#{message} at offset #{offset}")
10
72
  end
11
73
  end
12
74
  end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bindata"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Records
8
+ # A framed record whose body is read through a BinData class (BODY in the
9
+ # subclass). Bytes after the body (padding, unknown trailing data) are
10
+ # kept in `trailing`, so an unedited record re-encodes to its frame bytes.
11
+ class Base
12
+ attr_reader :type, :flags, :size, :data_size, :offset, :comment_index, :body, :trailing
13
+
14
+ def initialize(frame)
15
+ @type = frame.type
16
+ @flags = frame.flags
17
+ @size = frame.size
18
+ @data_size = frame.data_size
19
+ @offset = frame.offset
20
+ @comment_index = frame.comment_index
21
+ @body = read_body(frame)
22
+ @trailing = frame.data.byteslice(@body.num_bytes..).freeze
23
+ end
24
+
25
+ def to_binary
26
+ [type, flags, size, data_size].pack("vvVV") + body.to_binary_s + trailing
27
+ end
28
+
29
+ # Returns a new record with the given edits applied; the receiver is
30
+ # unchanged. Keys are:
31
+ # flags: the raw Record Flags value;
32
+ # flag fields: the names in #flag_fields (compressed:, emf_object_id:,
33
+ # combine_mode: ...). Booleans set or clear a bit, integers
34
+ # fill a bit range; every other flag bit is kept;
35
+ # body fields: any BinData field of the body. Length fields and
36
+ # their arrays are independent, so a variable-length
37
+ # edit sets both (item_count: 3, rect_data: {...}).
38
+ # Size and DataSize are recomputed. Trailing bytes beyond the original
39
+ # DataSize are alignment padding and are dropped (the new Size re-pads);
40
+ # trailing bytes inside it are unknown data, kept after the body and
41
+ # counted in DataSize.
42
+ # Changing a flag that selects the body layout (compressed, relative)
43
+ # keeps the other fields; array elements of the newly selected layout
44
+ # start zeroed unless the same call supplies them.
45
+ def with(**edits)
46
+ new_flags = edited_flags(edits)
47
+ new_body = edited_body(new_flags, edits)
48
+ self.class.new(edited_frame(new_flags, new_body))
49
+ end
50
+
51
+ # Flag-backed attributes editable through #with, as { name => mask }.
52
+ # Record families and mixins extend it with `super.merge(...)`.
53
+ def flag_fields = {}
54
+
55
+ private
56
+
57
+ def edited_flags(edits)
58
+ flag_edits = edits.slice(*flag_fields.keys)
59
+ flag_edits.reduce(edits.fetch(:flags, flags)) do |bits, (name, value)|
60
+ encode_flag(bits, name, flag_fields.fetch(name), value)
61
+ end
62
+ end
63
+
64
+ def encode_flag(bits, name, mask, value)
65
+ shift = (mask & -mask).bit_length - 1
66
+ number = { true => 1, false => 0 }.fetch(value, value)
67
+ unless number.is_a?(Integer) && number.between?(0, mask >> shift)
68
+ raise ArgumentError, "#{name} must fit #{(mask >> shift).bit_length} bit(s), got #{value.inspect}"
69
+ end
70
+
71
+ (bits & ~mask) | (number << shift)
72
+ end
73
+
74
+ def edited_body(new_flags, edits)
75
+ fields = edits.except(:flags, *flag_fields.keys)
76
+ unknown = fields.keys - body.field_names
77
+ raise ArgumentError, "unknown field(s) #{unknown.join(', ')} for #{self.class.name}" unless unknown.empty?
78
+
79
+ self.class::BODY.new(data_size: data_size, flags: new_flags).tap do |fresh|
80
+ fresh.assign(deep_merge(body.snapshot, fields))
81
+ end
82
+ end
83
+
84
+ # BinData#assign(hash) clears every field it is not given, so edits
85
+ # are folded into the body's own snapshot and assigned once.
86
+ def deep_merge(base, edit)
87
+ return edit unless base.is_a?(Hash) && edit.is_a?(Hash)
88
+
89
+ base.merge(edit) { |_name, old, new| deep_merge(old, new) }
90
+ end
91
+
92
+ def edited_frame(new_flags, new_body)
93
+ kept = trailing.byteslice(0, data_size - body.num_bytes)
94
+ Frame.new(type: type, flags: new_flags, size: Frame::HEADER_BYTES, data_size: 0, data: "",
95
+ offset: offset, comment_index: comment_index).with_data(new_body.to_binary_s + kept)
96
+ end
97
+
98
+ def read_body(frame)
99
+ self.class::BODY.new(data_size: data_size, flags: flags).read(frame.data.byteslice(0, data_size))
100
+ rescue IOError, BinData::ValidityError => e
101
+ raise ParseError.new(offset: offset, record_code: type,
102
+ message: "malformed #{self.class.name.split('::').last} body at offset #{offset}: #{e.message}")
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/emf_plus/records/primitives/rect_f"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Records
8
+ # EmfPlusBeginContainer (2.3.7.1). Flags low byte is the PageUnit
9
+ # (UnitType); DestRect and SrcRect define the container transform.
10
+ class BeginContainer < Base
11
+ TYPE = 0x4027
12
+ PAGE_UNIT_MASK = 0x00FF
13
+
14
+ class BeginContainerBody < Body
15
+ endian :little
16
+ rect_f :dest_rect
17
+ rect_f :src_rect
18
+ uint32 :stack_index
19
+ end
20
+ BODY = BeginContainerBody
21
+
22
+ def flag_fields = { page_unit: PAGE_UNIT_MASK }
23
+
24
+ def page_unit = flags & PAGE_UNIT_MASK
25
+ def dest_rect = body.dest_rect
26
+ def src_rect = body.src_rect
27
+ def stack_index = body.stack_index.to_i
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Records
6
+ # EmfPlusBeginContainerNoParams (2.3.7.2): opens a container identified by StackIndex.
7
+ class BeginContainerNoParams < Base
8
+ TYPE = 0x4028
9
+
10
+ class BeginContainerNoParamsBody < Body
11
+ endian :little
12
+ uint32 :stack_index
13
+ end
14
+ BODY = BeginContainerNoParamsBody
15
+
16
+ def stack_index = body.stack_index.to_i
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bindata"
4
+ require "emf/binary/class_field_accessors"
5
+
6
+ module Emf
7
+ module EmfPlus
8
+ module Records
9
+ # Base of every BinData structure declared under Records. Its fields are
10
+ # always class-level, so the accessors are defined once per class.
11
+ class Body < BinData::Record
12
+ prepend Emf::Binary::ClassFieldAccessors
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/emf_plus/records/primitives/argb"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Records
8
+ # EmfPlusClear (2.3.4.1). Flags are unused.
9
+ class Clear < Base
10
+ TYPE = 0x4009
11
+
12
+ class ClearBody < Body
13
+ endian :little
14
+ argb :color
15
+ end
16
+ BODY = ClearBody
17
+
18
+ def color = body.color
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Records
6
+ # Record Flags readers shared by the clipping and transform records.
7
+ module CombineModeFlags
8
+ COMBINE_MODE_MASK = 0x0F00
9
+ OBJECT_ID_MASK = 0x00FF
10
+
11
+ def flag_fields = super.merge(combine_mode: COMBINE_MODE_MASK, emf_object_id: OBJECT_ID_MASK)
12
+
13
+ def combine_mode = (flags & COMBINE_MODE_MASK) >> 8
14
+ def emf_object_id = flags & OBJECT_ID_MASK
15
+ end
16
+
17
+ module PostMultiplyFlag
18
+ POST_MULTIPLY_FLAG = 0x2000
19
+
20
+ def flag_fields = super.merge(post_multiply: POST_MULTIPLY_FLAG)
21
+
22
+ def post_multiply? = flags.anybits?(POST_MULTIPLY_FLAG)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Records
6
+ # EmfPlusComment (2.3.3.4): opaque private data.
7
+ class Comment < Base
8
+ TYPE = 0x4003
9
+
10
+ class CommentBody < Body
11
+ endian :little
12
+ mandatory_parameter :data_size
13
+ string :private_data, read_length: :data_size
14
+ end
15
+ BODY = CommentBody
16
+
17
+ def private_data = body.private_data.to_s
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+ require "emf/emf_plus/records/primitives/rect_array"
5
+
6
+ module Emf
7
+ module EmfPlus
8
+ module Records
9
+ # EmfPlusDrawArc (2.3.4.2). C selects Rect over RectF; ObjectID is the pen.
10
+ class DrawArc < Base
11
+ include ShapeFlags
12
+
13
+ TYPE = 0x4012
14
+
15
+ class DrawArcBody < Body
16
+ endian :little
17
+ mandatory_parameter :flags
18
+ float32_le :start_angle
19
+ float32_le :sweep_angle
20
+ rect_array :rect_data,
21
+ rect_count: 1,
22
+ compressed: -> { flags.anybits?(ShapeFlags::COMPRESSED_FLAG) }
23
+ end
24
+ BODY = DrawArcBody
25
+
26
+ def pen_id = emf_object_id
27
+ def start_angle = body.start_angle
28
+ def sweep_angle = body.sweep_angle
29
+ def rect = body.rect_data.rects.first
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/emf_plus/records/primitives/point_array"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Records
8
+ # EmfPlusDrawBeziers (2.3.4.3). C compressed, P relative points; ObjectID is the pen.
9
+ class DrawBeziers < Base
10
+ include ShapeFlags
11
+
12
+ TYPE = 0x4019
13
+
14
+ class DrawBeziersBody < Body
15
+ endian :little
16
+ mandatory_parameter :flags
17
+ uint32 :item_count
18
+ point_array :point_data,
19
+ point_count: :item_count,
20
+ relative: -> { flags.anybits?(ShapeFlags::RELATIVE_FLAG) },
21
+ compressed: -> { flags.anybits?(ShapeFlags::COMPRESSED_FLAG) }
22
+ end
23
+ BODY = DrawBeziersBody
24
+
25
+ def pen_id = emf_object_id
26
+ def count = body.item_count
27
+ def points = body.point_data.points
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+ require "emf/emf_plus/records/primitives/point_array"
5
+
6
+ module Emf
7
+ module EmfPlus
8
+ module Records
9
+ # EmfPlusDrawClosedCurve (2.3.4.4). C compressed, P relative points; ObjectID is the pen.
10
+ class DrawClosedCurve < Base
11
+ include ShapeFlags
12
+
13
+ TYPE = 0x4017
14
+
15
+ class DrawClosedCurveBody < Body
16
+ endian :little
17
+ mandatory_parameter :flags
18
+ float32_le :tension
19
+ uint32 :item_count
20
+ point_array :point_data,
21
+ point_count: :item_count,
22
+ relative: -> { flags.anybits?(ShapeFlags::RELATIVE_FLAG) },
23
+ compressed: -> { flags.anybits?(ShapeFlags::COMPRESSED_FLAG) }
24
+ end
25
+ BODY = DrawClosedCurveBody
26
+
27
+ def pen_id = emf_object_id
28
+ def tension = body.tension
29
+ def count = body.item_count
30
+ def points = body.point_data.points
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+ require "emf/emf_plus/records/primitives/point_array"
5
+
6
+ module Emf
7
+ module EmfPlus
8
+ module Records
9
+ # EmfPlusDrawCurve (2.3.4.5). C selects Point over PointF; ObjectID is the pen.
10
+ class DrawCurve < Base
11
+ include ShapeFlags
12
+
13
+ TYPE = 0x4018
14
+
15
+ class DrawCurveBody < Body
16
+ endian :little
17
+ mandatory_parameter :flags
18
+ float32_le :tension
19
+ uint32 :offset
20
+ uint32 :num_segments
21
+ uint32 :item_count
22
+ point_array :point_data,
23
+ point_count: :item_count,
24
+ relative: -> { false },
25
+ compressed: -> { flags.anybits?(ShapeFlags::COMPRESSED_FLAG) }
26
+ end
27
+ BODY = DrawCurveBody
28
+
29
+ def pen_id = emf_object_id
30
+ def tension = body.tension
31
+ def start_point_index = body.offset
32
+ def num_segments = body.num_segments
33
+ def count = body.item_count
34
+ def points = body.point_data.points
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+ require "emf/emf_plus/records/primitives/bounded_array"
5
+ require "emf/emf_plus/records/primitives/point_f"
6
+
7
+ module Emf
8
+ module EmfPlus
9
+ module Records
10
+ # EmfPlusDrawDriverString (2.3.4.6). The glyph position array always holds
11
+ # GlyphCount elements, as the Size and DataSize formulas require.
12
+ class DrawDriverString < Base
13
+ TYPE = 0x4036
14
+ SOLID_COLOR_FLAG = 0x8000
15
+ OBJECT_ID_MASK = 0x00FF
16
+ CMAP_LOOKUP = 0x00000001
17
+ VERTICAL = 0x00000002
18
+ REALIZED_ADVANCE = 0x00000004
19
+ LIMIT_SUBPIXEL = 0x00000008
20
+
21
+ class DrawDriverStringBody < Body
22
+ endian :little
23
+ uint32 :brush_id
24
+ uint32 :options_flags
25
+ uint32 :matrix_present
26
+ uint32 :glyph_count
27
+ bounded_array :glyphs, type: :uint16, element_size: 2, initial_length: :glyph_count
28
+ bounded_array :glyph_positions, type: :point_f, element_size: 8, initial_length: :glyph_count
29
+ array :transform_matrix, type: :float32_le, initial_length: 6, onlyif: -> { matrix_present == 1 }
30
+ end
31
+ BODY = DrawDriverStringBody
32
+
33
+ def flag_fields = { solid_color: SOLID_COLOR_FLAG, font_object_id: OBJECT_ID_MASK }
34
+
35
+ def solid_color? = flags.anybits?(SOLID_COLOR_FLAG)
36
+ def font_object_id = flags & OBJECT_ID_MASK
37
+ def brush_id = body.brush_id
38
+ def options_flags = body.options_flags
39
+ def cmap_lookup? = options_flags.anybits?(CMAP_LOOKUP)
40
+ def vertical? = options_flags.anybits?(VERTICAL)
41
+ def realized_advance? = options_flags.anybits?(REALIZED_ADVANCE)
42
+ def limit_subpixel? = options_flags.anybits?(LIMIT_SUBPIXEL)
43
+ def matrix_present? = body.matrix_present == 1
44
+ def glyph_count = body.glyph_count
45
+ def glyphs = body.glyphs.to_a
46
+
47
+ # Glyphs are UTF-16 code units only under CmapLookup; otherwise they are
48
+ # font glyph indices and carry no text.
49
+ def text
50
+ return unless cmap_lookup?
51
+
52
+ glyphs.pack("v*").force_encoding(Encoding::UTF_16LE)
53
+ .encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
54
+ end
55
+
56
+ def glyph_positions = body.glyph_positions.map { |p| [p.x, p.y] }
57
+ def transform_matrix = matrix_present? ? body.transform_matrix.to_a : nil
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/emf_plus/records/primitives/rect_array"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Records
8
+ # EmfPlusDrawEllipse (2.3.4.7). C selects Rect over RectF; ObjectID is the pen.
9
+ class DrawEllipse < Base
10
+ include ShapeFlags
11
+
12
+ TYPE = 0x400F
13
+
14
+ class DrawEllipseBody < Body
15
+ endian :little
16
+ mandatory_parameter :flags
17
+ rect_array :rect_data,
18
+ rect_count: 1,
19
+ compressed: -> { flags.anybits?(ShapeFlags::COMPRESSED_FLAG) }
20
+ end
21
+ BODY = DrawEllipseBody
22
+
23
+ def pen_id = emf_object_id
24
+ def rect = body.rect_data.rects.first
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/emf_plus/records/primitives/rect_f"
4
+ require "emf/emf_plus/records/primitives/rect"
5
+
6
+ module Emf
7
+ module EmfPlus
8
+ module Records
9
+ # EmfPlusDrawImage (2.3.4.8). The C flag selects EmfPlusRect over
10
+ # EmfPlusRectF for RectData.
11
+ class DrawImage < Base
12
+ TYPE = 0x401A
13
+ COMPRESSED_FLAG = 0x4000
14
+ OBJECT_ID_MASK = 0x00FF
15
+
16
+ class DrawImageBody < Body
17
+ endian :little
18
+ mandatory_parameter :flags
19
+ uint32 :image_attributes_id
20
+ int32 :src_unit
21
+ rect_f :src_rect
22
+ rect :compressed_rect, onlyif: -> { flags.anybits?(COMPRESSED_FLAG) }
23
+ rect_f :rect_data, onlyif: -> { !flags.anybits?(COMPRESSED_FLAG) }
24
+ end
25
+ BODY = DrawImageBody
26
+
27
+ def flag_fields = { compressed: COMPRESSED_FLAG, image_object_id: OBJECT_ID_MASK }
28
+
29
+ def compressed? = flags.anybits?(COMPRESSED_FLAG)
30
+ def image_object_id = flags & OBJECT_ID_MASK
31
+ def image_attributes_id = body.image_attributes_id
32
+ def src_unit = body.src_unit
33
+ def src_rect = rect_values(body.src_rect)
34
+ def dest_rect = rect_values(compressed? ? body.compressed_rect : body.rect_data)
35
+
36
+ private
37
+
38
+ def rect_values(rect) = [rect.x, rect.y, rect.width, rect.height]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/emf_plus/records/primitives/bounded_array"
4
+ require "emf/emf_plus/records/primitives/rect_f"
5
+ require "emf/emf_plus/records/primitives/point_f"
6
+ require "emf/emf_plus/records/primitives/point"
7
+ require "emf/emf_plus/records/primitives/point_r"
8
+
9
+ module Emf
10
+ module EmfPlus
11
+ module Records
12
+ # EmfPlusDrawImagePoints (2.3.4.9). P selects relative EmfPlusPointR
13
+ # elements and makes C undefined; otherwise C selects EmfPlusPoint over
14
+ # EmfPlusPointF. E means an earlier EmfPlusSerializableObject supplies
15
+ # the effect.
16
+ class DrawImagePoints < Base
17
+ TYPE = 0x401B
18
+ COMPRESSED_FLAG = 0x4000
19
+ EFFECT_FLAG = 0x2000
20
+ RELATIVE_FLAG = 0x0800
21
+ OBJECT_ID_MASK = 0x00FF
22
+
23
+ class DrawImagePointsBody < Body
24
+ endian :little
25
+ mandatory_parameter :flags
26
+ uint32 :image_attributes_id
27
+ int32 :src_unit
28
+ rect_f :src_rect
29
+ uint32 :point_count
30
+ bounded_array :relative_points, type: :point_r, element_size: 2, initial_length: :point_count, onlyif: lambda {
31
+ flags.anybits?(RELATIVE_FLAG)
32
+ }
33
+ bounded_array :compressed_points, type: :point, element_size: 4, initial_length: :point_count,
34
+ onlyif: -> { !flags.anybits?(RELATIVE_FLAG) && flags.anybits?(COMPRESSED_FLAG) }
35
+ bounded_array :float_points, type: :point_f, element_size: 8, initial_length: :point_count,
36
+ onlyif: -> { !flags.anybits?(RELATIVE_FLAG) && !flags.anybits?(COMPRESSED_FLAG) }
37
+ end
38
+ BODY = DrawImagePointsBody
39
+
40
+ def flag_fields
41
+ { compressed: COMPRESSED_FLAG, relative: RELATIVE_FLAG, effect: EFFECT_FLAG, image_object_id: OBJECT_ID_MASK }
42
+ end
43
+
44
+ def relative? = flags.anybits?(RELATIVE_FLAG)
45
+ def compressed? = !relative? && flags.anybits?(COMPRESSED_FLAG)
46
+ def effect? = flags.anybits?(EFFECT_FLAG)
47
+ def image_object_id = flags & OBJECT_ID_MASK
48
+ def image_attributes_id = body.image_attributes_id
49
+ def src_unit = body.src_unit
50
+ def src_rect = body.src_rect.then { |r| [r.x, r.y, r.width, r.height] }
51
+ def count = body.point_count
52
+ def points = relative? ? body.relative_points.map { |p| [p.x.to_i, p.y.to_i] } : point_array.map { |p| [p.x, p.y] }
53
+
54
+ private
55
+
56
+ def point_array
57
+ compressed? ? body.compressed_points : body.float_points
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end