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,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+ require "emf/emf_plus/records/primitives/palette"
5
+
6
+ module Emf
7
+ module EmfPlus
8
+ module Records
9
+ # EmfPlusSetTSGraphics (2.3.8.2). Flags bit T says an EmfPlusPalette
10
+ # follows the fixed part, bit V that it holds only basic VGA colours.
11
+ # `palette` is the raw palette bytes so any record re-encodes exactly;
12
+ # `decoded_palette` reads them as an EmfPlusPalette, or is nil when T is
13
+ # clear or the bytes are not exactly one palette.
14
+ class SetTSGraphics < Base
15
+ TYPE = 0x4039
16
+ PALETTE_FLAG = 0x0001
17
+ VGA_PALETTE_FLAG = 0x0002
18
+ FIXED_SIZE = 36
19
+
20
+ class SetTSGraphicsBody < Body
21
+ endian :little
22
+ mandatory_parameter :data_size
23
+ mandatory_parameter :flags
24
+ uint8 :anti_alias_mode
25
+ uint8 :text_render_hint
26
+ uint8 :compositing_mode
27
+ uint8 :compositing_quality
28
+ int16 :render_origin_x
29
+ int16 :render_origin_y
30
+ uint16 :text_contrast
31
+ uint8 :filter_type
32
+ uint8 :pixel_offset
33
+ array :world_to_device, type: :float32_le, initial_length: 6
34
+ string :palette, read_length: -> { flags.anybits?(PALETTE_FLAG) ? data_size - FIXED_SIZE : 0 }
35
+ end
36
+ BODY = SetTSGraphicsBody
37
+
38
+ def flag_fields = { palette: PALETTE_FLAG, vga_palette: VGA_PALETTE_FLAG }
39
+
40
+ def palette? = flags.anybits?(PALETTE_FLAG)
41
+ def vga_palette? = flags.anybits?(VGA_PALETTE_FLAG)
42
+ def anti_alias_mode = body.anti_alias_mode.to_i
43
+ def text_render_hint = body.text_render_hint.to_i
44
+ def compositing_mode = body.compositing_mode.to_i
45
+ def compositing_quality = body.compositing_quality.to_i
46
+ def render_origin_x = body.render_origin_x.to_i
47
+ def render_origin_y = body.render_origin_y.to_i
48
+ def text_contrast = body.text_contrast.to_i
49
+ def filter_type = body.filter_type.to_i
50
+ def pixel_offset = body.pixel_offset.to_i
51
+ def world_to_device = body.world_to_device.to_a
52
+ def palette = body.palette.to_s
53
+ alias palette_bytes palette
54
+
55
+ def decoded_palette
56
+ return unless palette?
57
+
58
+ decoded = Primitives::Palette.read(palette)
59
+ decoded if decoded.num_bytes == palette.bytesize
60
+ rescue IOError
61
+ nil
62
+ end
63
+
64
+ # Besides the base edits, `with(decoded_palette: {...})` takes a Hash of
65
+ # EmfPlusPalette fields: it sets T and replaces the palette bytes.
66
+ # PaletteCount defaults to the number of entries given.
67
+ private
68
+
69
+ def edited_flags(edits)
70
+ super(edits.key?(:decoded_palette) ? edits.merge(palette: true) : edits)
71
+ end
72
+
73
+ def edited_body(new_flags, edits)
74
+ return super unless edits.key?(:decoded_palette)
75
+
76
+ super(new_flags, edits.except(:decoded_palette)).tap do |fresh|
77
+ fresh.palette = palette_bytes_for(edits[:decoded_palette])
78
+ end
79
+ end
80
+
81
+ def palette_bytes_for(fields)
82
+ count = fields.fetch(:palette_count) { Array(fields[:palette_entries]).size }
83
+ Primitives::Palette.new(fields.merge(palette_count: count)).to_binary_s
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/emf_plus/records/primitives/transform_matrix"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Records
8
+ # EmfPlusSetWorldTransform (2.3.9.6): replaces the world transform; Flags are unused.
9
+ class SetWorldTransform < Base
10
+ TYPE = 0x402A
11
+
12
+ class SetWorldTransformBody < Body
13
+ endian :little
14
+ transform_matrix :matrix
15
+ end
16
+ BODY = SetWorldTransformBody
17
+
18
+ def matrix = body.matrix
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Records
6
+ # Record Flags bits shared by the drawing and fill records (2.3.4). The
7
+ # low byte is an object-table index whose meaning is per record.
8
+ module ShapeFlags
9
+ COMPRESSED_FLAG = 0x4000
10
+ RELATIVE_FLAG = 0x0800
11
+ OBJECT_ID_MASK = 0x00FF
12
+
13
+ def flag_fields
14
+ super.merge(compressed: COMPRESSED_FLAG, relative: RELATIVE_FLAG, emf_object_id: OBJECT_ID_MASK)
15
+ end
16
+
17
+ def compressed? = flags.anybits?(COMPRESSED_FLAG)
18
+ def relative? = flags.anybits?(RELATIVE_FLAG)
19
+ def emf_object_id = flags & OBJECT_ID_MASK
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "emf/binary/float32_le"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ module Records
8
+ # EmfPlusTranslateWorldTransform (2.3.9.7): translation by dx, dy.
9
+ class TranslateWorldTransform < Base
10
+ include PostMultiplyFlag
11
+
12
+ TYPE = 0x402D
13
+
14
+ class TranslateWorldTransformBody < Body
15
+ endian :little
16
+ float32_le :dx
17
+ float32_le :dy
18
+ end
19
+ BODY = TranslateWorldTransformBody
20
+
21
+ def dx = body.dx
22
+ def dy = body.dy
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ module Records
6
+ # A record type without a typed class; the frame is kept as is.
7
+ class Unknown
8
+ attr_reader :frame
9
+
10
+ def initialize(frame)
11
+ @frame = frame
12
+ end
13
+
14
+ def type = frame.type
15
+ def flags = frame.flags
16
+ def to_binary = frame.to_binary
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ # Typed views over framed EMF+ records. Types without a class decode to
6
+ # Records::Unknown, which keeps the frame so nothing is dropped.
7
+ module Records
8
+ autoload :Primitives, "emf/emf_plus/records/primitives"
9
+ autoload :Base, "emf/emf_plus/records/base"
10
+ autoload :Body, "emf/emf_plus/records/body"
11
+ autoload :Unknown, "emf/emf_plus/records/unknown"
12
+ autoload :Header, "emf/emf_plus/records/header"
13
+ autoload :EndOfFile, "emf/emf_plus/records/end_of_file"
14
+ autoload :Comment, "emf/emf_plus/records/comment"
15
+ autoload :GetDC, "emf/emf_plus/records/get_dc"
16
+ autoload :Object, "emf/emf_plus/records/object"
17
+
18
+ # Flag mixins shared by several record families.
19
+ autoload :ShapeFlags, "emf/emf_plus/records/shape_flags"
20
+ autoload :FillBrush, "emf/emf_plus/records/fill_brush"
21
+ autoload :CombineModeFlags, "emf/emf_plus/records/clip_transform_flags"
22
+ autoload :PostMultiplyFlag, "emf/emf_plus/records/clip_transform_flags"
23
+
24
+ # Drawing and fill
25
+ autoload :Clear, "emf/emf_plus/records/clear"
26
+ autoload :DrawRects, "emf/emf_plus/records/draw_rects"
27
+ autoload :DrawLines, "emf/emf_plus/records/draw_lines"
28
+ autoload :DrawEllipse, "emf/emf_plus/records/draw_ellipse"
29
+ autoload :DrawPie, "emf/emf_plus/records/draw_pie"
30
+ autoload :DrawArc, "emf/emf_plus/records/draw_arc"
31
+ autoload :DrawPath, "emf/emf_plus/records/draw_path"
32
+ autoload :DrawClosedCurve, "emf/emf_plus/records/draw_closed_curve"
33
+ autoload :DrawCurve, "emf/emf_plus/records/draw_curve"
34
+ autoload :DrawBeziers, "emf/emf_plus/records/draw_beziers"
35
+ autoload :FillRects, "emf/emf_plus/records/fill_rects"
36
+ autoload :FillPolygon, "emf/emf_plus/records/fill_polygon"
37
+ autoload :FillEllipse, "emf/emf_plus/records/fill_ellipse"
38
+ autoload :FillPie, "emf/emf_plus/records/fill_pie"
39
+ autoload :FillRegion, "emf/emf_plus/records/fill_region"
40
+ autoload :FillPath, "emf/emf_plus/records/fill_path"
41
+ autoload :FillClosedCurve, "emf/emf_plus/records/fill_closed_curve"
42
+
43
+ # State
44
+ autoload :SetRenderingOrigin, "emf/emf_plus/records/set_rendering_origin"
45
+ autoload :SetAntiAliasMode, "emf/emf_plus/records/set_anti_alias_mode"
46
+ autoload :SetTextRenderingHint, "emf/emf_plus/records/set_text_rendering_hint"
47
+ autoload :SetTextContrast, "emf/emf_plus/records/set_text_contrast"
48
+ autoload :SetInterpolationMode, "emf/emf_plus/records/set_interpolation_mode"
49
+ autoload :SetPixelOffsetMode, "emf/emf_plus/records/set_pixel_offset_mode"
50
+ autoload :SetCompositingMode, "emf/emf_plus/records/set_compositing_mode"
51
+ autoload :SetCompositingQuality, "emf/emf_plus/records/set_compositing_quality"
52
+ autoload :Save, "emf/emf_plus/records/save"
53
+ autoload :Restore, "emf/emf_plus/records/restore"
54
+ autoload :BeginContainer, "emf/emf_plus/records/begin_container"
55
+ autoload :BeginContainerNoParams, "emf/emf_plus/records/begin_container_no_params"
56
+ autoload :EndContainer, "emf/emf_plus/records/end_container"
57
+ autoload :SetTSGraphics, "emf/emf_plus/records/set_ts_graphics"
58
+
59
+ # Clipping and transform
60
+ autoload :ResetClip, "emf/emf_plus/records/reset_clip"
61
+ autoload :OffsetClip, "emf/emf_plus/records/offset_clip"
62
+ autoload :SetClipRect, "emf/emf_plus/records/set_clip_rect"
63
+ autoload :SetClipPath, "emf/emf_plus/records/set_clip_path"
64
+ autoload :SetClipRegion, "emf/emf_plus/records/set_clip_region"
65
+ autoload :SetTSClip, "emf/emf_plus/records/set_ts_clip"
66
+ autoload :SetWorldTransform, "emf/emf_plus/records/set_world_transform"
67
+ autoload :ResetWorldTransform, "emf/emf_plus/records/reset_world_transform"
68
+ autoload :MultiplyWorldTransform, "emf/emf_plus/records/multiply_world_transform"
69
+ autoload :TranslateWorldTransform, "emf/emf_plus/records/translate_world_transform"
70
+ autoload :ScaleWorldTransform, "emf/emf_plus/records/scale_world_transform"
71
+ autoload :RotateWorldTransform, "emf/emf_plus/records/rotate_world_transform"
72
+ autoload :SetPageTransform, "emf/emf_plus/records/set_page_transform"
73
+
74
+ # Text and image
75
+ autoload :DrawString, "emf/emf_plus/records/draw_string"
76
+ autoload :DrawDriverString, "emf/emf_plus/records/draw_driver_string"
77
+ autoload :DrawImage, "emf/emf_plus/records/draw_image"
78
+ autoload :DrawImagePoints, "emf/emf_plus/records/draw_image_points"
79
+ autoload :SerializableObject, "emf/emf_plus/records/serializable_object"
80
+
81
+ module_function
82
+
83
+ def registry
84
+ @registry ||= [
85
+ Header, EndOfFile, Comment, GetDC, Object,
86
+ Clear, DrawRects, DrawLines, DrawEllipse, DrawPie, DrawArc, DrawPath, DrawClosedCurve, DrawCurve, DrawBeziers,
87
+ FillRects, FillPolygon, FillEllipse, FillPie, FillRegion, FillPath, FillClosedCurve,
88
+ SetRenderingOrigin, SetAntiAliasMode, SetTextRenderingHint, SetTextContrast, SetInterpolationMode,
89
+ SetPixelOffsetMode, SetCompositingMode, SetCompositingQuality, Save, Restore, BeginContainer,
90
+ BeginContainerNoParams, EndContainer, SetTSGraphics,
91
+ ResetClip, OffsetClip, SetClipRect, SetClipPath, SetClipRegion, SetTSClip,
92
+ SetWorldTransform, ResetWorldTransform, MultiplyWorldTransform, TranslateWorldTransform,
93
+ ScaleWorldTransform, RotateWorldTransform, SetPageTransform,
94
+ DrawString, DrawDriverString, DrawImage, DrawImagePoints, SerializableObject
95
+ ].to_h { |klass| [klass::TYPE, klass] }.freeze
96
+ end
97
+
98
+ def decode(frame)
99
+ registry.fetch(frame.type, Unknown).new(frame)
100
+ end
101
+
102
+ def decode_all(frames)
103
+ frames.map { |frame| decode(frame) }
104
+ end
105
+ end
106
+ end
107
+ end
@@ -5,8 +5,15 @@ module Emf
5
5
  module Serializer
6
6
  module_function
7
7
 
8
- def call(_metafile)
9
- raise SerializeError, "EMF+ serializer not yet implemented — see TODO.impl/15-public-api.md"
8
+ def call(frames)
9
+ frames.map(&:to_binary).join.b
10
+ end
11
+
12
+ # Re-serializes frames per originating comment: { record index => payload
13
+ # bytes (without the "EMF+" identifier) }, ready to write back into the
14
+ # matching EMR_COMMENT.
15
+ def by_comment(frames)
16
+ frames.group_by(&:comment_index).transform_values { |group| call(group) }
10
17
  end
11
18
  end
12
19
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ class Stream
6
+ # Reports records MS-EMFPLUS 2.1.1.1 does not allow in a conformant file.
7
+ # Only reports: the records stay Records::Unknown with their bytes intact.
8
+ # `kind` is :reserved_type, :stroke_fill_path, :unknown_type or
9
+ # :header_not_after_emf_header.
10
+ module Conformance
11
+ Diagnostic = Struct.new(:kind, :comment_index, :offset, :record_type, :message, keyword_init: true) do
12
+ def initialize(**)
13
+ super
14
+ freeze
15
+ end
16
+ end
17
+
18
+ RESERVED = (0x4005..0x4007)
19
+ STROKE_FILL_PATH = 0x4037
20
+ DEFINED = (0x4001..0x403A)
21
+
22
+ module_function
23
+
24
+ HEADER_PLACEMENT = "EmfPlusHeader record MUST be embedded in an EMR_COMMENT_EMFPLUS record, " \
25
+ "which MUST be the record immediately following the EMF header (MS-EMFPLUS 2.3.3.3)"
26
+
27
+ def check(handles, segments)
28
+ (type_diagnostics(handles, segments) + header_diagnostics(handles, segments)).freeze
29
+ end
30
+
31
+ def type_diagnostics(handles, segments)
32
+ handles.filter_map do |handle|
33
+ next unless handle.record.is_a?(Records::Unknown)
34
+
35
+ kind, message = classify(handle.record.type)
36
+ next unless kind
37
+
38
+ Diagnostic.new(kind: kind, comment_index: segments[handle.segment_index].record_index,
39
+ offset: handle.offset, record_type: handle.record.type, message: message)
40
+ end
41
+ end
42
+
43
+ # Only the first header is placed: the text does not forbid further
44
+ # headers, and corpus files carry a second one in a later comment.
45
+ def header_diagnostics(handles, segments)
46
+ handle = handles.find { |candidate| candidate.record.is_a?(Records::Header) }
47
+ comment_index = handle && segments[handle.segment_index].record_index
48
+ return [] if handle.nil? || comment_index.zero?
49
+
50
+ [Diagnostic.new(kind: :header_not_after_emf_header, comment_index: comment_index, offset: handle.offset,
51
+ record_type: handle.record.type, message: HEADER_PLACEMENT)]
52
+ end
53
+
54
+ def classify(type)
55
+ if RESERVED.cover?(type)
56
+ [:reserved_type, format("reserved EMF+ record type 0x%04X MUST NOT be used", type)]
57
+ elsif type == STROKE_FILL_PATH
58
+ [:stroke_fill_path, "EmfPlusStrokeFillPath (0x4037) has no record layout"]
59
+ elsif !DEFINED.cover?(type)
60
+ [:unknown_type, format("EMF+ record type 0x%04X is outside 0x4001-0x403A", type)]
61
+ end
62
+ end
63
+
64
+ private_class_method :type_diagnostics, :header_diagnostics, :classify
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ class Stream
6
+ # Collects record replacements for Stream#edit.
7
+ class Editor
8
+ attr_reader :stream
9
+
10
+ def initialize(stream)
11
+ @stream = stream
12
+ end
13
+
14
+ def replace(handle, record)
15
+ @stream = @stream.with_record(handle, record)
16
+ self
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ class Stream
6
+ # The EMF records (indices into Metafile#records) lying between two
7
+ # EMF+-bearing comments, or before the first / after the last one.
8
+ class EmfGap
9
+ attr_reader :range
10
+
11
+ def initialize(range)
12
+ @range = range
13
+ freeze
14
+ end
15
+
16
+ def gap? = true
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ class Stream
6
+ # The Links view of a stream's records: GetDC spans, effect links by draw
7
+ # handle and link diagnostics.
8
+ class LinkIndex
9
+ # A record handle seen through the framed-record interface Links reads.
10
+ Frame = Struct.new(:handle, :comment_index) do
11
+ def type = handle.record.type
12
+ def flags = handle.record.flags
13
+ def offset = handle.offset
14
+ end
15
+ private_constant :Frame
16
+
17
+ attr_reader :dc_spans, :errors
18
+
19
+ def initialize(source, handles, segments)
20
+ frames = handles.map { |handle| Frame.new(handle, segments[handle.segment_index].record_index) }
21
+ links, @errors = Links.effect_links(frames)
22
+ @effects = links.to_h { |link| [link.draw.handle.stream_index, link.effect.handle] }.freeze
23
+ @dc_spans = Links.get_dc_spans(source, frames).freeze
24
+ end
25
+
26
+ def effect_for(handle) = @effects[handle.stream_index]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ class Stream
6
+ # Builds the edited Metafile: dirty segments' comments are rewritten, every
7
+ # other record is reused, and the header size follows the byte change.
8
+ class Rebuild
9
+ def initialize(source, dirty_segments)
10
+ @source = source
11
+ @dirty_segments = dirty_segments
12
+ end
13
+
14
+ def call
15
+ records = @source.records.dup
16
+ delta = @dirty_segments.sum { |segment| rewrite_comment(records, segment) }
17
+ Model::Metafile.new(format: @source.format, header: resized_header(delta), records: records,
18
+ errors: @source.errors, emf_plus: emf_plus_of(records), trailing: @source.trailing)
19
+ end
20
+
21
+ private
22
+
23
+ # Replaces the segment's comment in `records`; returns the change in bytes.
24
+ def rewrite_comment(records, segment)
25
+ original = @source.records[segment.record_index]
26
+ wire = segment.comment_wire(original.wire)
27
+ records[segment.record_index] = Model::Emr::Records::WireAdapter.new(wire: wire, offset: original.offset)
28
+ wire.n_size - original.wire.n_size
29
+ end
30
+
31
+ def resized_header(delta)
32
+ return @source.header if delta.zero?
33
+
34
+ wire = @source.header.to_wire
35
+ wire.n_bytes = @source.header.n_bytes + delta
36
+ Model::Emr::Header.from_wire(wire)
37
+ end
38
+
39
+ def emf_plus_of(records)
40
+ records.filter_map do |record|
41
+ Emf::Emr::Parser.extract_emf_plus(record.wire) if record.wire.is_a?(Emf::Emr::Binary::Records::Comment)
42
+ end.join.b
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ class Stream
6
+ # A typed EMF+ record with its position: the segment it lives in, its byte
7
+ # offset inside that segment's payload and its index among all records.
8
+ # `comment_offset` is the file offset of the containing EMR_COMMENT, or nil
9
+ # when the source record has none (a rebuilt or hand-made metafile).
10
+ class RecordHandle
11
+ attr_reader :record, :segment_index, :offset, :stream_index, :comment_offset
12
+
13
+ def initialize(record:, segment_index:, offset:, stream_index:, comment_offset: nil)
14
+ @record = record
15
+ @segment_index = segment_index
16
+ @offset = offset
17
+ @stream_index = stream_index
18
+ @comment_offset = comment_offset
19
+ freeze
20
+ end
21
+
22
+ def gap? = false
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module EmfPlus
5
+ class Stream
6
+ # The EMF+ payload of one EMR_COMMENT. `record_index` locates the comment
7
+ # in Metafile#records and `payload_range` the payload inside its wire
8
+ # data (after the "EMF+" identifier). Bytes the framer could not read
9
+ # (`unframed`) and bytes after cb_data (`tail`) are kept verbatim.
10
+ class Segment
11
+ IDENTIFIER = "EMF+".b.freeze
12
+
13
+ attr_reader :record_index, :payload_range, :records, :unframed, :tail
14
+
15
+ # Returns nil unless the comment carries an EMF+ payload.
16
+ def self.from_comment(record_index, wire, errors)
17
+ data = wire.data.b
18
+ return unless wire.cb_data >= IDENTIFIER.bytesize && data.start_with?(IDENTIFIER)
19
+
20
+ length = [wire.cb_data, data.bytesize].min - IDENTIFIER.bytesize
21
+ payload = data.byteslice(IDENTIFIER.bytesize, length)
22
+ frames = Parser.call(payload, errors: errors, comment_index: record_index)
23
+ framed = frames.sum(&:size)
24
+ payload_end = IDENTIFIER.bytesize + length
25
+ new(record_index: record_index, payload_range: IDENTIFIER.bytesize...payload_end, records: decode(frames, errors),
26
+ unframed: payload.byteslice(framed..), tail: data.byteslice(payload_end..))
27
+ end
28
+
29
+ def self.decode(frames, errors)
30
+ frames.map do |frame|
31
+ Records.decode(frame)
32
+ rescue ParseError => e
33
+ errors << e
34
+ Records::Unknown.new(frame)
35
+ end
36
+ end
37
+
38
+ def initialize(record_index:, payload_range:, records:, unframed:, tail:, dirty: false)
39
+ @record_index = record_index
40
+ @payload_range = payload_range
41
+ @records = records.freeze
42
+ @unframed = unframed.freeze
43
+ @tail = tail.freeze
44
+ @dirty = dirty
45
+ freeze
46
+ end
47
+
48
+ def dirty? = @dirty
49
+
50
+ def payload
51
+ records.map(&:to_binary).join.b + unframed
52
+ end
53
+
54
+ def replace(position, record)
55
+ swapped = records.dup.tap { |list| list[position] = record }
56
+ self.class.new(record_index: record_index, payload_range: payload_range, records: swapped,
57
+ unframed: unframed, tail: tail, dirty: true)
58
+ end
59
+
60
+ # The EMR_COMMENT wire for this segment with cb_data and nSize recomputed.
61
+ def comment_wire(original)
62
+ data = IDENTIFIER + payload + tail
63
+ padded = data + ("\0" * (-data.bytesize % 4))
64
+ Emf::Emr::Binary::Records::Comment.new(i_type: original.i_type, n_size: 12 + padded.bytesize,
65
+ cb_data: IDENTIFIER.bytesize + payload.bytesize, data: padded)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end