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
@@ -7,9 +7,136 @@ module Emf
7
7
  module Emr
8
8
  module Binary
9
9
  module Records
10
- # Stub: preserves full body bytes. Full layout lands with TODO 10.
10
+ # Exposes option-dependent text buffers without changing the wire layout.
11
11
  class PolyTextOutA < Emf::Emr::Binary::Record
12
12
  rest :body
13
+
14
+ def c_strings
15
+ checked_body(32).byteslice(28, 4).unpack1("V")
16
+ end
17
+
18
+ def bounds
19
+ checked_body(16).byteslice(0, 16).b
20
+ end
21
+
22
+ def bounds=(value)
23
+ replace_body_bytes(0, 16, value)
24
+ end
25
+
26
+ def text_bytes(index)
27
+ read_span(text_span(index))
28
+ end
29
+
30
+ def set_text_bytes(index, value)
31
+ replace_body_bytes(*text_span(index), value)
32
+ end
33
+
34
+ def dx_bytes(index)
35
+ span = dx_span(index)
36
+ read_span(span) if span
37
+ end
38
+
39
+ def set_dx_bytes(index, value)
40
+ span = dx_span(index)
41
+ raise ArgumentError, "text entry has no spacing buffer" unless span
42
+
43
+ replace_body_bytes(*span, value)
44
+ end
45
+
46
+ def entry_bounds(index)
47
+ span = entry_bounds_span(index)
48
+ read_span(span) if span
49
+ end
50
+
51
+ def set_entry_bounds(index, value)
52
+ span = entry_bounds_span(index)
53
+ raise ArgumentError, "text entry has no bounds rectangle" unless span
54
+
55
+ replace_body_bytes(*span, value)
56
+ end
57
+
58
+ private
59
+
60
+ def checked_body(required)
61
+ bytes = body.to_s
62
+ raise ArgumentError, "truncated poly text output body" if bytes.bytesize < required
63
+
64
+ bytes
65
+ end
66
+
67
+ def entry_location(index)
68
+ bytes = checked_body(32)
69
+ count = bytes.byteslice(28, 4).unpack1("V")
70
+ raise ArgumentError, "invalid text entry index" unless index.is_a?(Integer) && index >= 0 && index < count
71
+ raise ArgumentError, "truncated text entry table" if count > (bytes.bytesize - 32) / 24
72
+
73
+ offset = 32
74
+ selected = nil
75
+ count.times do |position|
76
+ raise ArgumentError, "truncated text entry table" if bytes.bytesize - offset < 24
77
+
78
+ options = bytes.byteslice(offset + 16, 4).unpack1("V")
79
+ length = options.nobits?(0x100) ? 40 : 24
80
+ raise ArgumentError, "truncated text entry table" if bytes.bytesize - offset < length
81
+
82
+ selected = offset if position == index
83
+ offset += length
84
+ end
85
+ [bytes, selected, offset]
86
+ end
87
+
88
+ def text_span(index)
89
+ bytes, offset, table_end = entry_location(index)
90
+ count, pointer = bytes.byteslice(offset + 8, 8).unpack("V2")
91
+ buffer_span(bytes, pointer, count, table_end, 1)
92
+ end
93
+
94
+ def dx_span(index)
95
+ bytes, offset, table_end = entry_location(index)
96
+ count = bytes.byteslice(offset + 8, 4).unpack1("V")
97
+ options = bytes.byteslice(offset + 16, 4).unpack1("V")
98
+ pointer_at = options.nobits?(0x100) ? 36 : 20
99
+ pointer = bytes.byteslice(offset + pointer_at, 4).unpack1("V")
100
+ return nil if pointer.zero?
101
+
102
+ width = options.nobits?(0x2000) ? 4 : 8
103
+ buffer_span(bytes, pointer, count * width, table_end, 4)
104
+ end
105
+
106
+ def entry_bounds_span(index)
107
+ bytes, offset, = entry_location(index)
108
+ options = bytes.byteslice(offset + 16, 4).unpack1("V")
109
+ [offset + 20, 16] if options.nobits?(0x100)
110
+ end
111
+
112
+ def buffer_span(bytes, pointer, length, table_end, alignment)
113
+ return [0, 0] if length.zero?
114
+
115
+ raise ArgumentError, "misaligned text entry buffer" unless (pointer % alignment).zero?
116
+
117
+ offset = pointer - 8
118
+ if offset < table_end || offset > bytes.bytesize || length > bytes.bytesize - offset
119
+ raise ArgumentError, "invalid text entry buffer span"
120
+ end
121
+
122
+ [offset, length]
123
+ end
124
+
125
+ def read_span(span)
126
+ offset, length = span
127
+ body.to_s.byteslice(offset, length).b
128
+ end
129
+
130
+ def replace_body_bytes(offset, length, value)
131
+ bytes = checked_body(offset + length)
132
+ unless value.is_a?(String) && value.bytesize == length
133
+ raise ArgumentError, "poly text output value must be a String of #{length} bytes"
134
+ end
135
+
136
+ replacement = bytes.b
137
+ replacement[offset, length] = value.b
138
+ self.body = replacement
139
+ end
13
140
  end
14
141
  end
15
142
  end
@@ -7,9 +7,136 @@ module Emf
7
7
  module Emr
8
8
  module Binary
9
9
  module Records
10
- # Stub: preserves full body bytes. Full layout lands with TODO 10.
10
+ # Exposes option-dependent text buffers without changing the wire layout.
11
11
  class PolyTextOutW < Emf::Emr::Binary::Record
12
12
  rest :body
13
+
14
+ def c_strings
15
+ checked_body(32).byteslice(28, 4).unpack1("V")
16
+ end
17
+
18
+ def bounds
19
+ checked_body(16).byteslice(0, 16).b
20
+ end
21
+
22
+ def bounds=(value)
23
+ replace_body_bytes(0, 16, value)
24
+ end
25
+
26
+ def text_bytes(index)
27
+ read_span(text_span(index))
28
+ end
29
+
30
+ def set_text_bytes(index, value)
31
+ replace_body_bytes(*text_span(index), value)
32
+ end
33
+
34
+ def dx_bytes(index)
35
+ span = dx_span(index)
36
+ read_span(span) if span
37
+ end
38
+
39
+ def set_dx_bytes(index, value)
40
+ span = dx_span(index)
41
+ raise ArgumentError, "text entry has no spacing buffer" unless span
42
+
43
+ replace_body_bytes(*span, value)
44
+ end
45
+
46
+ def entry_bounds(index)
47
+ span = entry_bounds_span(index)
48
+ read_span(span) if span
49
+ end
50
+
51
+ def set_entry_bounds(index, value)
52
+ span = entry_bounds_span(index)
53
+ raise ArgumentError, "text entry has no bounds rectangle" unless span
54
+
55
+ replace_body_bytes(*span, value)
56
+ end
57
+
58
+ private
59
+
60
+ def checked_body(required)
61
+ bytes = body.to_s
62
+ raise ArgumentError, "truncated poly text output body" if bytes.bytesize < required
63
+
64
+ bytes
65
+ end
66
+
67
+ def entry_location(index)
68
+ bytes = checked_body(32)
69
+ count = bytes.byteslice(28, 4).unpack1("V")
70
+ raise ArgumentError, "invalid text entry index" unless index.is_a?(Integer) && index >= 0 && index < count
71
+ raise ArgumentError, "truncated text entry table" if count > (bytes.bytesize - 32) / 24
72
+
73
+ offset = 32
74
+ selected = nil
75
+ count.times do |position|
76
+ raise ArgumentError, "truncated text entry table" if bytes.bytesize - offset < 24
77
+
78
+ options = bytes.byteslice(offset + 16, 4).unpack1("V")
79
+ length = options.nobits?(0x100) ? 40 : 24
80
+ raise ArgumentError, "truncated text entry table" if bytes.bytesize - offset < length
81
+
82
+ selected = offset if position == index
83
+ offset += length
84
+ end
85
+ [bytes, selected, offset]
86
+ end
87
+
88
+ def text_span(index)
89
+ bytes, offset, table_end = entry_location(index)
90
+ count, pointer = bytes.byteslice(offset + 8, 8).unpack("V2")
91
+ buffer_span(bytes, pointer, count * 2, table_end, 2)
92
+ end
93
+
94
+ def dx_span(index)
95
+ bytes, offset, table_end = entry_location(index)
96
+ count = bytes.byteslice(offset + 8, 4).unpack1("V")
97
+ options = bytes.byteslice(offset + 16, 4).unpack1("V")
98
+ pointer_at = options.nobits?(0x100) ? 36 : 20
99
+ pointer = bytes.byteslice(offset + pointer_at, 4).unpack1("V")
100
+ return nil if pointer.zero?
101
+
102
+ width = options.nobits?(0x2000) ? 4 : 8
103
+ buffer_span(bytes, pointer, count * width, table_end, 4)
104
+ end
105
+
106
+ def entry_bounds_span(index)
107
+ bytes, offset, = entry_location(index)
108
+ options = bytes.byteslice(offset + 16, 4).unpack1("V")
109
+ [offset + 20, 16] if options.nobits?(0x100)
110
+ end
111
+
112
+ def buffer_span(bytes, pointer, length, table_end, alignment)
113
+ return [0, 0] if length.zero?
114
+
115
+ raise ArgumentError, "misaligned text entry buffer" unless (pointer % alignment).zero?
116
+
117
+ offset = pointer - 8
118
+ if offset < table_end || offset > bytes.bytesize || length > bytes.bytesize - offset
119
+ raise ArgumentError, "invalid text entry buffer span"
120
+ end
121
+
122
+ [offset, length]
123
+ end
124
+
125
+ def read_span(span)
126
+ offset, length = span
127
+ body.to_s.byteslice(offset, length).b
128
+ end
129
+
130
+ def replace_body_bytes(offset, length, value)
131
+ bytes = checked_body(offset + length)
132
+ unless value.is_a?(String) && value.bytesize == length
133
+ raise ArgumentError, "poly text output value must be a String of #{length} bytes"
134
+ end
135
+
136
+ replacement = bytes.b
137
+ replacement[offset, length] = value.b
138
+ self.body = replacement
139
+ end
13
140
  end
14
141
  end
15
142
  end
@@ -12,6 +12,9 @@ module Emf
12
12
  uint32 :i_type
13
13
  uint32 :n_size
14
14
  rest :payload
15
+
16
+ # Not a Record, so it has no built fast path: just its bytes.
17
+ def append_built_to?(_out) = false
15
18
  end
16
19
  end
17
20
  end
@@ -8,6 +8,7 @@ module Emf
8
8
  module Binary
9
9
  module Records
10
10
  class RealizePalette < Emf::Emr::Binary::Record
11
+ rest :trailing
11
12
  end
12
13
  end
13
14
  end
@@ -10,7 +10,7 @@ module Emf
10
10
  # EMR_RECTANGLE: emr + rclBox per MS-EMF 2.3.5.
11
11
  # No rclBounds.
12
12
  class Rectangle < Emf::Emr::Binary::Record
13
- rectl :rcl_box
13
+ rectl :rcl_box
14
14
  end
15
15
  end
16
16
  end
@@ -2,14 +2,28 @@
2
2
 
3
3
  require "bindata"
4
4
  require "emf/emr/binary/record"
5
+ require "emf/emr/binary/palette_body"
5
6
 
6
7
  module Emf
7
8
  module Emr
8
9
  module Binary
9
10
  module Records
10
- # Stub: preserves full body bytes. Full layout lands with TODO 10.
11
11
  class ResizePalette < Emf::Emr::Binary::Record
12
- rest :body
12
+ uint32 :ih_pal
13
+ uint32 :entry_count
14
+ rest :trailing
15
+
16
+ def body
17
+ PaletteBody.new(self, to_binary_s.byteslice(8..))
18
+ end
19
+
20
+ def body=(value)
21
+ bytes = value.to_s
22
+ raise ArgumentError, "invalid palette body length" if bytes.bytesize < 8
23
+
24
+ replacement = self.class.read(to_binary_s.byteslice(0, 8) + bytes)
25
+ assign(replacement.snapshot)
26
+ end
13
27
  end
14
28
  end
15
29
  end
@@ -10,8 +10,8 @@ module Emf
10
10
  # EMR_ROUNDRECT: emr + rclBox per MS-EMF 2.3.5.
11
11
  # No rclBounds.
12
12
  class RoundRect < Emf::Emr::Binary::Record
13
- rectl :rcl_box
14
- size_l :szl_corner
13
+ rectl :rcl_box
14
+ size_l :szl_corner
15
15
  end
16
16
  end
17
17
  end
@@ -9,6 +9,7 @@ module Emf
9
9
  module Records
10
10
  class SelectPalette < Emf::Emr::Binary::Record
11
11
  uint32 :ih_pal
12
+ rest :trailing
12
13
  end
13
14
  end
14
15
  end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bindata"
4
+ require "emf/emr/binary/record"
5
+
6
+ module Emf
7
+ module Emr
8
+ module Binary
9
+ module Records
10
+ class SetColorAdjustment < Emf::Emr::Binary::Record
11
+ rest :body
12
+
13
+ def adjustment_size
14
+ read_adjustment_value(0, "v")
15
+ end
16
+
17
+ def adjustment_size=(value)
18
+ write_adjustment_value(0, value)
19
+ end
20
+
21
+ def values
22
+ read_adjustment_value(2, "v")
23
+ end
24
+
25
+ def values=(value)
26
+ write_adjustment_value(2, value)
27
+ end
28
+
29
+ def illuminant_index
30
+ read_adjustment_value(4, "v")
31
+ end
32
+
33
+ def illuminant_index=(value)
34
+ write_adjustment_value(4, value)
35
+ end
36
+
37
+ def red_gamma
38
+ read_adjustment_value(6, "v")
39
+ end
40
+
41
+ def red_gamma=(value)
42
+ write_adjustment_value(6, value)
43
+ end
44
+
45
+ def green_gamma
46
+ read_adjustment_value(8, "v")
47
+ end
48
+
49
+ def green_gamma=(value)
50
+ write_adjustment_value(8, value)
51
+ end
52
+
53
+ def blue_gamma
54
+ read_adjustment_value(10, "v")
55
+ end
56
+
57
+ def blue_gamma=(value)
58
+ write_adjustment_value(10, value)
59
+ end
60
+
61
+ def reference_black
62
+ read_adjustment_value(12, "v")
63
+ end
64
+
65
+ def reference_black=(value)
66
+ write_adjustment_value(12, value)
67
+ end
68
+
69
+ def reference_white
70
+ read_adjustment_value(14, "v")
71
+ end
72
+
73
+ def reference_white=(value)
74
+ write_adjustment_value(14, value)
75
+ end
76
+
77
+ def contrast
78
+ read_adjustment_value(16, "s<")
79
+ end
80
+
81
+ def contrast=(value)
82
+ write_adjustment_value(16, value, signed: true)
83
+ end
84
+
85
+ def brightness
86
+ read_adjustment_value(18, "s<")
87
+ end
88
+
89
+ def brightness=(value)
90
+ write_adjustment_value(18, value, signed: true)
91
+ end
92
+
93
+ def colorfulness
94
+ read_adjustment_value(20, "s<")
95
+ end
96
+
97
+ def colorfulness=(value)
98
+ write_adjustment_value(20, value, signed: true)
99
+ end
100
+
101
+ def red_green_tint
102
+ read_adjustment_value(22, "s<")
103
+ end
104
+
105
+ def red_green_tint=(value)
106
+ write_adjustment_value(22, value, signed: true)
107
+ end
108
+
109
+ private
110
+
111
+ def adjustment_body
112
+ bytes = body.to_s
113
+ raise ArgumentError, "invalid color adjustment body length" if bytes.bytesize < 24
114
+
115
+ bytes
116
+ end
117
+
118
+ def read_adjustment_value(offset, format)
119
+ adjustment_body.byteslice(offset, 2).unpack1(format)
120
+ end
121
+
122
+ def write_adjustment_value(offset, value, signed: false)
123
+ bytes = adjustment_body.dup
124
+ raise ArgumentError, "color adjustment value must be an Integer" unless value.is_a?(Integer)
125
+
126
+ bounds = signed ? (-32_768..32_767) : (0..65_535)
127
+ raise RangeError, "color adjustment value must fit its 16-bit integer field" unless bounds.cover?(value)
128
+
129
+ bytes[offset, 2] = [value].pack(signed ? "s<" : "v")
130
+ self.body = bytes
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -7,9 +7,30 @@ module Emf
7
7
  module Emr
8
8
  module Binary
9
9
  module Records
10
- # Stub: preserves full body bytes. Full layout lands with TODO 10.
11
10
  class SetColorspace < Emf::Emr::Binary::Record
12
11
  rest :body
12
+
13
+ def ih_cs
14
+ colorspace_body.byteslice(0, 4).unpack1("V")
15
+ end
16
+
17
+ def ih_cs=(value)
18
+ bytes = colorspace_body.dup
19
+ raise ArgumentError, "colorspace handle must be an Integer" unless value.is_a?(Integer)
20
+ raise RangeError, "colorspace handle must fit an unsigned 32-bit integer" unless (0..0xffffffff).cover?(value)
21
+
22
+ bytes[0, 4] = [value].pack("V")
23
+ self.body = bytes
24
+ end
25
+
26
+ private
27
+
28
+ def colorspace_body
29
+ bytes = body.to_s
30
+ raise ArgumentError, "invalid colorspace body length" if bytes.bytesize < 4
31
+
32
+ bytes
33
+ end
13
34
  end
14
35
  end
15
36
  end
@@ -1,15 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bindata"
4
+ require "emf/emr/binary/variable_buffers"
4
5
  require "emf/emr/binary/with_bounds"
6
+ require "emf/emr/binary/dib_header"
5
7
 
6
8
  module Emf
7
9
  module Emr
8
10
  module Binary
9
11
  module Records
10
12
  # Stub: leading fields per MS-EMF, body captures remaining bytes.
13
+ # The fixed part is 76 bytes including the EMR header; the bitmap
14
+ # pointers are read from body without declaring fields.
11
15
  class SetDibitsToDevice < Emf::Emr::Binary::WithBounds
16
+ include Emf::Emr::Binary::VariableBuffers
17
+
18
+ fixed_layout 76
19
+ variable_buffer :bmi_bytes, offset: :off_bmi_src, length: :cb_bmi_src
20
+ variable_buffer :bits_bytes, offset: :off_bits_src, length: :cb_bits_src
21
+
12
22
  rest :body
23
+
24
+ # Read-only accessors computed from body on each call; body starts at
25
+ # record offset 24, so a record offset maps to (offset - 24) in body.
26
+ def off_bmi_src
27
+ pointers(24)[0]
28
+ end
29
+
30
+ def cb_bmi_src
31
+ pointers(24)[1]
32
+ end
33
+
34
+ def off_bits_src
35
+ pointers(24)[2]
36
+ end
37
+
38
+ def cb_bits_src
39
+ pointers(24)[3]
40
+ end
41
+
42
+ def dib_header_view
43
+ DibHeader.view(off_bmi_src, cb_bmi_src, 76, after_fixed,
44
+ bits_offset: off_bits_src, bits_size: cb_bits_src)
45
+ end
46
+
47
+ private
48
+
49
+ def after_fixed
50
+ body.to_s.byteslice(52..).to_s
51
+ end
52
+
53
+ def pointers(offset)
54
+ body.to_s.byteslice(offset, 16).to_s.unpack("V4").map(&:to_i)
55
+ end
13
56
  end
14
57
  end
15
58
  end
@@ -1,15 +1,85 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bindata"
4
+ require "emf/emr/binary/variable_buffers"
4
5
  require "emf/emr/binary/record"
5
6
 
6
7
  module Emf
7
8
  module Emr
8
9
  module Binary
9
10
  module Records
10
- # Stub: preserves full body bytes. Full layout lands with TODO 10.
11
+ # MS-EMF 2.3.11.15: name and profile lengths are byte counts.
11
12
  class SetIcmProfileA < Emf::Emr::Binary::Record
13
+ include Emf::Emr::Binary::VariableBuffers
14
+
15
+ fixed_layout 20
16
+ variable_buffer :icm_name_bytes, offset: 20, length: -> { u32_at(12) }
17
+ variable_buffer :icm_profile_bytes, offset: -> { 20 + u32_at(12) }, length: -> { u32_at(16) }
18
+
19
+ # Byte counts and opaque spans retain the original body layout.
12
20
  rest :body
21
+
22
+ def dw_flags
23
+ profile_prefix.unpack1("V")
24
+ end
25
+
26
+ def dw_flags=(value)
27
+ raise ArgumentError, "color profile flags must be an Integer" unless value.is_a?(Integer)
28
+ raise RangeError, "color profile flags must fit an unsigned 32-bit integer" unless (0..0xFFFFFFFF).cover?(value)
29
+
30
+ write_profile_bytes(0, 4, [value].pack("V"))
31
+ end
32
+
33
+ def cb_name
34
+ profile_prefix.byteslice(4, 4).unpack1("V")
35
+ end
36
+
37
+ def cb_data
38
+ profile_prefix.byteslice(8, 4).unpack1("V")
39
+ end
40
+
41
+ def name_bytes
42
+ read_profile_bytes(12, cb_name)
43
+ end
44
+
45
+ def name_bytes=(value)
46
+ write_profile_bytes(12, cb_name, value)
47
+ end
48
+
49
+ def profile_bytes
50
+ read_profile_bytes(12 + cb_name, cb_data)
51
+ end
52
+
53
+ def profile_bytes=(value)
54
+ write_profile_bytes(12 + cb_name, cb_data, value)
55
+ end
56
+
57
+ private
58
+
59
+ def profile_prefix
60
+ bytes = body.to_s
61
+ raise ArgumentError, "truncated color profile prefix" if bytes.bytesize < 12
62
+
63
+ bytes
64
+ end
65
+
66
+ def read_profile_bytes(offset, length)
67
+ bytes = profile_prefix
68
+ raise ArgumentError, "truncated color profile span" if offset + length > bytes.bytesize
69
+
70
+ bytes.byteslice(offset, length).b
71
+ end
72
+
73
+ def write_profile_bytes(offset, length, value)
74
+ read_profile_bytes(offset, length)
75
+ unless value.is_a?(String) && value.bytesize == length
76
+ raise ArgumentError, "color profile value must be a String of #{length} bytes"
77
+ end
78
+
79
+ bytes = body.to_s.b
80
+ bytes[offset, length] = value.b
81
+ self.body = bytes
82
+ end
13
83
  end
14
84
  end
15
85
  end