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,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Emf
6
+ module EmfPlus
7
+ # A lazy, typed view of the EMF+ records of a parsed Metafile. Reading never
8
+ # changes the Metafile; edits yield a new Metafile in which only the
9
+ # comments of edited segments are rebuilt. Framing problems are collected in
10
+ # #errors (never raised, never added to Metafile#errors) and the affected
11
+ # bytes stay in the segment, so nothing is dropped.
12
+ class Stream
13
+ include Enumerable
14
+
15
+ autoload :Segment, "emf/emf_plus/stream/segment"
16
+ autoload :RecordHandle, "emf/emf_plus/stream/record_handle"
17
+ autoload :EmfGap, "emf/emf_plus/stream/emf_gap"
18
+ autoload :Rebuild, "emf/emf_plus/stream/rebuild"
19
+ autoload :Editor, "emf/emf_plus/stream/editor"
20
+ autoload :LinkIndex, "emf/emf_plus/stream/link_index"
21
+ autoload :Conformance, "emf/emf_plus/stream/conformance"
22
+
23
+ attr_reader :source, :segments
24
+
25
+ def self.from_metafile(metafile)
26
+ errors = []
27
+ segments = metafile.records.each_with_index.filter_map do |record, index|
28
+ Segment.from_comment(index, record.wire, errors) if record.wire.is_a?(Emf::Emr::Binary::Records::Comment)
29
+ end
30
+ new(source: metafile, segments: segments, errors: errors, digests: digests_of(metafile, segments))
31
+ end
32
+
33
+ def self.digests_of(metafile, segments)
34
+ segments.map { |segment| Digest::SHA256.digest(metafile.records[segment.record_index].to_binary_s) }
35
+ end
36
+ private_class_method :digests_of
37
+
38
+ def initialize(source:, segments:, errors:, digests:)
39
+ @source = source
40
+ @segments = segments.freeze
41
+ @framing_errors = errors.freeze
42
+ @digests = digests.freeze
43
+ end
44
+
45
+ def ok? = errors.empty?
46
+
47
+ # Framing problems, then object table, effect link and conformance diagnostics.
48
+ def errors
49
+ @errors ||= (@framing_errors + objects.errors + links.errors + Conformance.check(records, segments)).freeze
50
+ end
51
+
52
+ # EMF+ records and EMF gaps in file order.
53
+ def entries
54
+ @entries ||= build_entries.freeze
55
+ end
56
+
57
+ def each(&block)
58
+ return to_enum(:each) unless block
59
+
60
+ entries.each(&block)
61
+ end
62
+
63
+ def each_record(&block)
64
+ return to_enum(:each_record) unless block
65
+
66
+ entries.each { |entry| yield entry unless entry.gap? }
67
+ end
68
+
69
+ def records = each_record.to_a
70
+
71
+ # True once a source comment's bytes differ from what this stream was built from.
72
+ def stale?
73
+ segments.each_with_index.any? do |segment, index|
74
+ Digest::SHA256.digest(source.records[segment.record_index].to_binary_s) != @digests[index]
75
+ end
76
+ end
77
+
78
+ def dirty? = segments.any?(&:dirty?)
79
+
80
+ # Returns a new Stream with `handle`'s record replaced; the receiver is unchanged.
81
+ def with_record(handle, new_record)
82
+ assert_fresh!
83
+ segment = segments.fetch(handle.segment_index)
84
+ position = segment.records.index { |record| record.equal?(handle.record) }
85
+ raise ArgumentError, "record does not belong to this stream" unless position
86
+
87
+ swapped = segments.dup.tap { |list| list[handle.segment_index] = segment.replace(position, new_record) }
88
+ self.class.new(source: source, segments: swapped, errors: @framing_errors, digests: @digests)
89
+ end
90
+
91
+ # Yields an Editor (#replace(handle, record)) and returns the edited Metafile.
92
+ def edit
93
+ editor = Editor.new(self)
94
+ yield editor
95
+ editor.stream.to_metafile
96
+ end
97
+
98
+ # The source itself when nothing was edited.
99
+ def to_metafile
100
+ assert_fresh!
101
+ return source unless dirty?
102
+
103
+ Rebuild.new(source, segments.select(&:dirty?)).call
104
+ end
105
+
106
+ def assert_fresh!
107
+ raise StaleStreamError, "the source metafile's EMF+ comments changed after this stream was built" if stale?
108
+ end
109
+
110
+ # The object table over this stream's records in stream order.
111
+ def objects
112
+ @objects ||= ObjectTable.new(records.map(&:record))
113
+ end
114
+
115
+ # Resolutions of the object references `handle`'s record carries.
116
+ def resolve(handle) = objects.resolve(handle.record, at: handle.stream_index)
117
+
118
+ def dc_spans = links.dc_spans
119
+
120
+ # The SerializableObject handle linked to an E-flag draw handle, or nil.
121
+ def effect_for(handle) = links.effect_for(handle)
122
+
123
+ private
124
+
125
+ def links
126
+ @links ||= LinkIndex.new(source, records, segments)
127
+ end
128
+
129
+ def build_entries
130
+ stream_index = 0
131
+ cursor = 0
132
+ segments.each_with_index.flat_map do |segment, segment_index|
133
+ gap = gap_before(cursor, segment.record_index)
134
+ cursor = segment.record_index + 1
135
+ offset = 0
136
+ comment_offset = source.records[segment.record_index].then { |record| record.offset if record.respond_to?(:offset) }
137
+ handles = segment.records.map do |record|
138
+ handle = RecordHandle.new(record: record, segment_index: segment_index, offset: offset, stream_index: stream_index,
139
+ comment_offset: comment_offset)
140
+ offset += record.to_binary.bytesize
141
+ stream_index += 1
142
+ handle
143
+ end
144
+ gap + handles
145
+ end + gap_before(cursor, source.records.size)
146
+ end
147
+
148
+ def gap_before(from, upto)
149
+ from < upto ? [EmfGap.new(from...upto)] : []
150
+ end
151
+ end
152
+ end
153
+ end
data/lib/emf/emf_plus.rb CHANGED
@@ -5,5 +5,14 @@ module Emf
5
5
  autoload :Parser, "emf/emf_plus/parser"
6
6
  autoload :Serializer, "emf/emf_plus/serializer"
7
7
  autoload :Binary, "emf/emf_plus/binary"
8
+ autoload :Frame, "emf/emf_plus/frame"
9
+ autoload :Records, "emf/emf_plus/records"
10
+ autoload :ObjectChain, "emf/emf_plus/object_chain"
11
+ autoload :ObjectTable, "emf/emf_plus/object_table"
12
+ autoload :Objects, "emf/emf_plus/objects"
13
+ autoload :Effects, "emf/emf_plus/effects"
14
+ autoload :Stream, "emf/emf_plus/stream"
15
+ autoload :StaleStreamError, "emf/error"
16
+ autoload :Links, "emf/emf_plus/links"
8
17
  end
9
18
  end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module Emr
5
+ module Binary
6
+ # Read-only typed view of the DIB header (BITMAPCOREHEADER,
7
+ # BITMAPINFOHEADER, BITMAPV4HEADER, BITMAPV5HEADER) embedded in an EMR
8
+ # bitmap record, plus the bounds of its colour table. The record bytes
9
+ # are never touched; a malformed record yields a view with no header and
10
+ # a reason instead of raising.
11
+ class DibHeader < Struct.new(
12
+ :kind, :header_size, :width, :height, :planes, :bit_count, :compression, :size_image,
13
+ :x_pels_per_meter, :y_pels_per_meter, :clr_used, :clr_important,
14
+ :bitfield_mask_count, :color_table_offset, :color_table_entries, :color_table_bytes,
15
+ keyword_init: true
16
+ )
17
+ # header is nil whenever reason is set. The pixel array is judged
18
+ # separately so a header problem is never masked: bits_reason is nil
19
+ # when the header is unusable (the stride needs it) or the bits are
20
+ # sound, and valid? keeps meaning "the header is usable".
21
+ View = Struct.new(:header, :reason, :bits_offset, :bits_size, :bits_reason) do
22
+ def valid?
23
+ !header.nil?
24
+ end
25
+
26
+ def bits_valid?
27
+ valid? && bits_reason.nil?
28
+ end
29
+ end
30
+
31
+ KINDS = { 12 => :core, 40 => :info, 108 => :v4, 124 => :v5 }.freeze
32
+ BI_RGB = 0
33
+ BI_BITFIELDS = 3
34
+ BI_ALPHABITFIELDS = 6
35
+ BI_JPEG = 4
36
+ BI_PNG = 5
37
+ UNCOMPRESSED = [BI_RGB, BI_BITFIELDS, BI_ALPHABITFIELDS].freeze
38
+ private_constant :KINDS, :BI_RGB, :BI_BITFIELDS, :BI_ALPHABITFIELDS, :BI_JPEG, :BI_PNG, :UNCOMPRESSED
39
+
40
+ # offset/size are the record's offBmi/cbBmi (from record start);
41
+ # fixed is the byte length of the declared fixed part including the
42
+ # 8-byte EMR header; tail is the opaque remainder after it.
43
+ # bits_offset/bits_size are the record's offBits/cbBits.
44
+ def self.view(offset, size, fixed, tail, bits_offset: 0, bits_size: 0)
45
+ offset = offset.to_i
46
+ size = size.to_i
47
+ tail = tail.to_s
48
+ bits = [bits_offset.to_i, bits_size.to_i]
49
+ return View.new(nil, :absent, *bits) if size.zero?
50
+ return View.new(nil, :offset_in_fixed_part, *bits) if offset < fixed
51
+
52
+ start = offset - fixed
53
+ return View.new(nil, :past_record, *bits) if start + size > tail.bytesize
54
+
55
+ with_bits(build(tail.byteslice(start, size)), offset, size, fixed, tail.bytesize, bits)
56
+ end
57
+
58
+ def self.with_bits(view, offset, size, fixed, tail_size, bits)
59
+ view.bits_offset, view.bits_size = bits
60
+ view.bits_reason = bits_reason(view.header, offset, size, fixed, tail_size, bits) if view.valid?
61
+ view
62
+ end
63
+
64
+ def self.bits_reason(header, offset, size, fixed, tail_size, bits)
65
+ bits_offset, bits_size = bits
66
+ return :bits_absent if bits_size.zero?
67
+ return :bits_in_fixed_part if bits_offset < fixed
68
+ return :bits_past_record if bits_offset - fixed + bits_size > tail_size
69
+ return :bits_overlap_bmi if bits_offset < offset + size && offset < bits_offset + bits_size
70
+
71
+ :bits_smaller_than_image if bits_size < minimum_bits(header)
72
+ end
73
+
74
+ # Only uncompressed rows have a stride-derived minimum. RLE4/RLE8 runs
75
+ # can be far shorter than stride * height, and for BI_JPEG/BI_PNG
76
+ # (MS-WMF 2.2.2.3 BitmapInfoHeader) the pixel array is the encoded
77
+ # image, so bit_count does not describe it.
78
+ def self.minimum_bits(header)
79
+ return 0 unless UNCOMPRESSED.include?(header.compression)
80
+
81
+ (((header.width.abs * header.bit_count) + 31) / 32) * 4 * header.height.abs
82
+ end
83
+
84
+ def self.build(bmi)
85
+ return View.new(nil, :truncated) if bmi.bytesize < 4
86
+
87
+ header_size = bmi.unpack1("V")
88
+ kind = KINDS[header_size]
89
+ return View.new(nil, :unsupported_header_size) unless kind
90
+ return View.new(nil, :bmi_smaller_than_header) if bmi.bytesize < header_size
91
+
92
+ header = kind == :core ? core(bmi) : info(bmi, kind)
93
+ fits = header.color_table_offset + header.color_table_bytes <= bmi.bytesize
94
+ fits ? View.new(header, nil) : View.new(nil, :color_table_past_bmi)
95
+ end
96
+
97
+ def self.core(bmi)
98
+ width, height, planes, bit_count = bmi.unpack("x4v4")
99
+ make(:core, 12, width, height, planes, bit_count, 0, 0, 0, 0, 0, 0, 3)
100
+ end
101
+
102
+ def self.info(bmi, kind)
103
+ width, height, planes, bit_count, compression, size_image, xppm, yppm, clr_used, clr_important = bmi.unpack("x4l<2v2V2l<2V2")
104
+ make(kind, KINDS.key(kind), width, height, planes, bit_count, compression, size_image, xppm, yppm, clr_used, clr_important, 4)
105
+ end
106
+
107
+ def self.make(kind, header_size, width, height, planes, bit_count, compression, size_image, xppm, yppm, used, important, entry_size)
108
+ masks = mask_count(kind, compression)
109
+ entries = table_entries(kind, bit_count, compression, used)
110
+ new(kind: kind, header_size: header_size, width: width, height: height, planes: planes, bit_count: bit_count,
111
+ compression: compression, size_image: size_image, x_pels_per_meter: xppm, y_pels_per_meter: yppm,
112
+ clr_used: used, clr_important: important, bitfield_mask_count: masks,
113
+ color_table_offset: header_size + (masks * 4),
114
+ color_table_entries: entries, color_table_bytes: entries * entry_size)
115
+ end
116
+
117
+ # A BITMAPINFOHEADER with BI_BITFIELDS/BI_ALPHABITFIELDS stores the
118
+ # channel masks between the header and the colour table; V4/V5
119
+ # headers carry them inside the header itself.
120
+ def self.mask_count(kind, compression)
121
+ return 0 unless kind == :info
122
+
123
+ { BI_BITFIELDS => 3, BI_ALPHABITFIELDS => 4 }.fetch(compression, 0)
124
+ end
125
+
126
+ def self.table_entries(kind, bit_count, compression, used)
127
+ return 0 if kind == :info && compressed_image?(compression)
128
+ return used if bit_count > 8
129
+
130
+ used.zero? ? 1 << bit_count : used
131
+ end
132
+
133
+ def self.compressed_image?(compression)
134
+ [BI_JPEG, BI_PNG].include?(compression)
135
+ end
136
+
137
+ private_class_method :with_bits, :bits_reason, :minimum_bits, :compressed_image?, :build, :core, :info, :make,
138
+ :mask_count, :table_entries
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module Emr
5
+ module Binary
6
+ # Reads the EMRTEXT string and Dx array of EMR_EXTTEXTOUTA/W from the
7
+ # record bytes. Offsets are from record start; a missing or out-of-range
8
+ # part is nil, never an error.
9
+ module ExtTextOutFields
10
+ ETO_NO_RECT = 0x100
11
+ ETO_PDY = 0x2000
12
+
13
+ # Raw string bytes (nChars * #char_size at offString), or nil.
14
+ def string_bytes
15
+ slice_of_record(off_string, n_chars * char_size)
16
+ end
17
+
18
+ # The string as UTF-8, or nil. See the A/W classes for the decoding.
19
+ def text
20
+ bytes = string_bytes
21
+ bytes && decode_text(bytes)
22
+ end
23
+
24
+ # Dx array: unsigned 32-bit values at offDx, nChars of them (2 * nChars
25
+ # with ETO_PDY), or nil when offDx is 0 or the range leaves the record.
26
+ def dx
27
+ count = n_chars * (f_options.anybits?(ETO_PDY) ? 2 : 1)
28
+ bytes = slice_of_record(off_dx, count * 4)
29
+ bytes&.unpack("V*")
30
+ end
31
+
32
+ private
33
+
34
+ def slice_of_record(offset, length)
35
+ return unless offset.positive? && length.positive?
36
+
37
+ bytes = to_binary_s
38
+ bytes.byteslice(offset, length) if offset + length <= bytes.bytesize
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bindata"
4
+
5
+ module Emf
6
+ module Emr
7
+ module Binary
8
+ class ExtendedPenBuffers < BinData::Record
9
+ mandatory_parameters :fixed_size, :first_offset, :first_length, :second_offset, :second_length
10
+ string :first_padding, read_length: -> { [eval_parameter(:first_offset) - eval_parameter(:fixed_size), 0].max }
11
+ string :first_bitmap, read_length: :first_length
12
+ string :second_padding, read_length: lambda {
13
+ finish = [eval_parameter(:first_offset), eval_parameter(:fixed_size)].max + eval_parameter(:first_length)
14
+ [eval_parameter(:second_offset) - finish, 0].max
15
+ }
16
+ string :second_bitmap, read_length: :second_length
17
+ rest :trailing
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ BinData::RegisteredClasses.register("extended_pen_buffers", Emf::Emr::Binary::ExtendedPenBuffers)