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