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,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module Emr
5
+ module Binary
6
+ # Straight-line `pack` for a PackPlan: one local per leaf, inline range
7
+ # checks, one Array#pack with a frozen format. It takes only symbol-keyed
8
+ # Hashes of known, in-range, non-nil values (nested structs as Hashes or
9
+ # as instances of their own class; the record's array as the plan takes
10
+ # it); for anything else (unknown keys, nil values, bad values) it hands
11
+ # the call to `pack_interpreted`, so declines and results stay the
12
+ # interpreter's.
13
+ module PackPlanGenerate
14
+ FALLBACK = "return pack_interpreted(attrs, size: size)"
15
+ private_constant :FALLBACK
16
+
17
+ private
18
+
19
+ def generate_pack
20
+ @gen_depth = 0
21
+ @gen_classes = []
22
+ fields = @tree.reject { |_, node| node.equal?(@array) }
23
+ body = fields.map { |name, node| gen_node(name, node, "attrs", "seen", " ") }.join
24
+ values = (0...@leaves.size).map { |index| "v#{index}" }
25
+ values << "*coords" if @array
26
+ format = (@array ? @points_format : @format).inspect
27
+ # def pack(attrs, size: true)
28
+ # seen = 0
29
+ # <one read-and-check block per field>
30
+ # return pack_interpreted(attrs, size: size) unless seen == attrs.size
31
+ # v1 = <size> if size
32
+ # [v0, v1, ...].pack("<frozen format>")
33
+ # end
34
+ source = <<~RUBY
35
+ # frozen_string_literal: true
36
+ def pack(attrs, size: true)
37
+ #{gen_exact} seen = 0
38
+ #{body}#{gen_array} #{FALLBACK} unless seen == attrs.size
39
+
40
+ #{gen_size} [#{values.join(', ')}].pack(#{format})
41
+ end
42
+ RUBY
43
+ singleton_class.class_eval(source, __FILE__, __LINE__)
44
+ end
45
+
46
+ def gen_array
47
+ return "" unless @array
48
+
49
+ <<~RUBY.gsub(/^(?=.)/, " ")
50
+ coords = attrs[#{@array_name.inspect}]
51
+ unless coords.nil?
52
+ seen += 1
53
+ coords = catch(:decline) { coords_of(coords) } or #{FALLBACK}
54
+ end
55
+ #{FALLBACK} unless v#{@count} == (coords ? coords.size / 2 : 0)
56
+ RUBY
57
+ end
58
+
59
+ def gen_size
60
+ bytes = @array ? " + (coords ? coords.size * #{@coord_bytes} : 0)" : ""
61
+ " v#{@n_size} = #{@size}#{bytes} if size\n"
62
+ end
63
+
64
+ def gen_node(name, node, container, counter, indent)
65
+ return gen_leaf(gen_read(container, name, indent), node.index, counter, indent) if node.is_a?(PackPlan::Leaf)
66
+
67
+ sub = "h#{@gen_depth += 1}"
68
+ @gen_classes << node.klass
69
+ klass = "@gen_classes[#{@gen_classes.size - 1}]"
70
+ inner = node.children.map { |child, n| gen_node(child, n, sub, "#{sub}_seen", "#{indent} ") }.join
71
+ "#{indent}#{sub} = #{container}[#{name.inspect}]\n" \
72
+ "#{indent}if #{sub}.instance_of?(#{klass})\n#{indent} #{counter} += 1\n#{gen_from(node, sub, "#{indent} ")}" \
73
+ "#{indent}else\n#{gen_hash(sub, counter, inner, indent)}#{indent}end\n"
74
+ end
75
+
76
+ def gen_hash(sub, counter, inner, indent)
77
+ "#{indent} if #{sub}.nil? then #{sub} = {}\n" \
78
+ "#{indent} elsif #{sub}.instance_of?(Hash) then #{counter} += 1\n" \
79
+ "#{indent} else #{FALLBACK}\n#{indent} end\n" \
80
+ "#{indent} #{sub}_seen = 0\n#{inner}" \
81
+ "#{indent} #{FALLBACK} unless #{sub}_seen == #{sub}.size\n"
82
+ end
83
+
84
+ # The leaves of a struct instance, read through `snapshot`.
85
+ def gen_from(branch, source, indent)
86
+ branch.children.map do |name, node|
87
+ if node.is_a?(PackPlan::Leaf)
88
+ gen_leaf("#{indent}v#{node.index} = #{source}.#{name}.snapshot\n", node.index, nil, indent)
89
+ else
90
+ gen_from(node, "#{source}.#{name}", indent)
91
+ end
92
+ end.join
93
+ end
94
+
95
+ def gen_read(container, name, indent) = "#{indent}v = #{container}[#{name.inspect}]\n"
96
+
97
+ def gen_leaf(read, index, counter, indent)
98
+ var = "v#{index}"
99
+ read = read.sub(/\bv = /, "#{var} = ")
100
+ return "#{read}#{indent}#{FALLBACK} unless #{gen_valid(var, index)}\n" unless counter
101
+
102
+ "#{read}#{indent}if #{var}.nil? then #{var} = #{@defaults[index].inspect}\n" \
103
+ "#{indent}elsif #{gen_valid(var, index)} then #{counter} += 1\n" \
104
+ "#{indent}else #{FALLBACK}\n#{indent}end\n"
105
+ end
106
+
107
+ def gen_valid(var, index)
108
+ low = @lows[index]
109
+ return "float32_exact?(#{var})" unless low
110
+
111
+ "#{var}.instance_of?(Integer) && #{var} >= #{low} && #{var} <= #{@highs[index]}"
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module Emr
5
+ module Binary
6
+ # Which attribute values a PackPlan takes: only those BinData stores
7
+ # unchanged, so the wire read back from the bytes is the wire it built.
8
+ module PackPlanValues
9
+ private
10
+
11
+ def store(values, node, value)
12
+ case node
13
+ when PackPlan::Leaf then store_leaf(values, node.index, value)
14
+ else store_branch(values, node, value)
15
+ end
16
+ end
17
+
18
+ def store_leaf(values, index, value)
19
+ low = @lows[index]
20
+ if low
21
+ return unless value.instance_of?(Integer) && value >= low && value <= @highs[index]
22
+ else
23
+ return unless float32_exact?(value)
24
+ end
25
+ values[index] = value
26
+ end
27
+
28
+ # A Float a float32 leaf stores unchanged: it reads back as the same
29
+ # bits (so not NaN, not past float32 range, not rounded).
30
+ def float32_exact?(value)
31
+ value.instance_of?(Float) && [value].pack("E") == [[value].pack("e").unpack1("e")].pack("E")
32
+ end
33
+
34
+ def store_branch(values, node, value)
35
+ if value.instance_of?(Hash)
36
+ value.each do |name, leaf|
37
+ child = node.children[name] or return false
38
+ next if leaf.nil?
39
+
40
+ store(values, child, leaf) or return false
41
+ end
42
+ true
43
+ elsif value.instance_of?(node.klass)
44
+ node.children.all? { |name, child| store_from(values, child, value.public_send(name)) }
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ def store_from(values, node, source)
51
+ return store_branch_from(values, node, source) if node.is_a?(PackPlan::Branch)
52
+
53
+ store_leaf(values, node.index, source.snapshot)
54
+ end
55
+
56
+ def store_branch_from(values, node, source)
57
+ node.children.all? { |name, child| store_from(values, child, source.public_send(name)) }
58
+ end
59
+
60
+ # Flat coordinates of an Array of {x:, y:} Hashes or points of the
61
+ # record's point class, all Integers in the point range; else declines.
62
+ # Each point is read in place: no per-point pair is allocated.
63
+ def coords_of(points)
64
+ throw :decline unless points.instance_of?(Array)
65
+
66
+ coords = []
67
+ points.each do |point|
68
+ if point.instance_of?(Hash)
69
+ throw :decline unless point.size == 2
70
+
71
+ x = point[:x]
72
+ y = point[:y]
73
+ elsif point.instance_of?(@point_class)
74
+ x = point.x.snapshot
75
+ y = point.y.snapshot
76
+ else
77
+ throw :decline
78
+ end
79
+ throw :decline unless in_point_range?(x) && in_point_range?(y)
80
+
81
+ coords << x << y
82
+ end
83
+ coords
84
+ end
85
+
86
+ def in_point_range?(value)
87
+ value.instance_of?(Integer) && value >= @coord_low && value <= @coord_high
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Emf
4
+ module Emr
5
+ module Binary
6
+ class PaletteBody < String
7
+ def initialize(record, bytes)
8
+ @record = record
9
+ super(bytes)
10
+ end
11
+
12
+ def assign(bytes)
13
+ @record.body = bytes
14
+ replace(@record.body)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bindata"
4
+
5
+ module Emf
6
+ module Emr
7
+ module Binary
8
+ class EmrPaletteEntry < BinData::Record
9
+ uint8 :red
10
+ uint8 :green
11
+ uint8 :blue
12
+ uint8 :flags
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ BinData::RegisteredClasses.register("emr_palette_entry", Emf::Emr::Binary::EmrPaletteEntry)
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bindata"
4
+ require "emf/binary/class_field_accessors"
5
+ require "emf/emr/binary/thin_shell"
4
6
 
5
7
  module Emf
6
8
  module Emr
@@ -8,9 +10,40 @@ module Emf
8
10
  # All EMR records share an 8-byte header: uint32 iType, uint32 nSize.
9
11
  # Subclasses add their own fields after this.
10
12
  class Record < BinData::Record
13
+ include ThinShell
14
+ prepend Emf::Binary::ClassFieldAccessors
15
+
16
+ # An assignment into the bytes a shell is still made from is taken
17
+ # before the field is made; anything else is the generic assignment.
18
+ module PackedAssignment
19
+ private
20
+
21
+ def assign_field_at(index, val)
22
+ return val if (@field_objs.nil? || @field_objs[index].nil?) && packed_assign(index, val)
23
+
24
+ super
25
+ end
26
+ end
27
+ prepend PackedAssignment
28
+
11
29
  endian :little
12
30
  uint32 :i_type
13
31
  uint32 :n_size
32
+
33
+ NO_ARG = Object.new.freeze
34
+ private_constant :NO_ARG
35
+
36
+ # `Klass.new(**attrs)` for a fixed-layout record builds from the
37
+ # FastFill template; every other call shape is BinData's constructor.
38
+ # BinData takes at most three positionals (value, parameters,
39
+ # parent), named here so the hot call allocates no splat array.
40
+ def self.new(value = NO_ARG, parameters = NO_ARG, parent = NO_ARG, **attrs)
41
+ if NO_ARG.equal?(value) && !attrs.empty? && (filler = FastFill.for_class(self))
42
+ wire = filler.build_packed(attrs) || filler.build(attrs)
43
+ return wire if wire
44
+ end
45
+ super(*[value, parameters, parent].take_while { |arg| !NO_ARG.equal?(arg) }, **attrs)
46
+ end
14
47
  end
15
48
  end
16
49
  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 108 bytes including the EMR header; the bitmap
14
+ # pointers are read from body without declaring fields.
11
15
  class AlphaBlend < Emf::Emr::Binary::WithBounds
16
+ include Emf::Emr::Binary::VariableBuffers
17
+
18
+ fixed_layout 108
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(60)[0]
28
+ end
29
+
30
+ def cb_bmi_src
31
+ pointers(60)[1]
32
+ end
33
+
34
+ def off_bits_src
35
+ pointers(60)[2]
36
+ end
37
+
38
+ def cb_bits_src
39
+ pointers(60)[3]
40
+ end
41
+
42
+ def dib_header_view
43
+ DibHeader.view(off_bmi_src, cb_bmi_src, 108, 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(84..).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
@@ -10,8 +10,8 @@ module Emf
10
10
  class AngleArc < Emf::Emr::Binary::Record
11
11
  point_l :ptl_center
12
12
  uint32 :n_radius
13
- float :start_angle
14
- float :sweep_angle
13
+ float32_le :start_angle
14
+ float32_le :sweep_angle
15
15
  end
16
16
  end
17
17
  end
@@ -1,7 +1,9 @@
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
@@ -16,6 +18,12 @@ module Emf
16
18
  # a fixed-length :string (NOT :rest, which would consume the
17
19
  # whole tail).
18
20
  class BitBlt < Emf::Emr::Binary::WithBounds
21
+ include Emf::Emr::Binary::VariableBuffers
22
+
23
+ fixed_layout 100
24
+ variable_buffer :bmi_bytes, offset: :off_bmi_src, length: :cb_bmi_src
25
+ variable_buffer :bits_bytes, offset: :off_bits_src, length: :cb_bits_src
26
+
19
27
  int32le :x_dest
20
28
  int32le :y_dest
21
29
  int32le :cx_dest
@@ -30,6 +38,12 @@ module Emf
30
38
  uint32le :off_bits_src
31
39
  uint32le :cb_bits_src
32
40
  rest :body
41
+
42
+ # Read-only; computed from the current fields on each call.
43
+ def dib_header_view
44
+ DibHeader.view(off_bmi_src, cb_bmi_src, 100, body,
45
+ bits_offset: off_bits_src, bits_size: cb_bits_src)
46
+ end
33
47
  end
34
48
  end
35
49
  end
@@ -1,15 +1,69 @@
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
11
  class ColorCorrectPalette < Emf::Emr::Binary::Record
12
+ include Emf::Emr::Binary::VariableBuffers
13
+
14
+ fixed_layout 24
15
+
12
16
  rest :body
17
+
18
+ def ih_palette
19
+ palette_body.byteslice(0, 4).unpack1("V")
20
+ end
21
+
22
+ def ih_palette=(value)
23
+ write_palette_value(0, value)
24
+ end
25
+
26
+ def first_entry
27
+ palette_body.byteslice(4, 4).unpack1("V")
28
+ end
29
+
30
+ def first_entry=(value)
31
+ write_palette_value(4, value)
32
+ end
33
+
34
+ def entry_count
35
+ palette_body.byteslice(8, 4).unpack1("V")
36
+ end
37
+
38
+ def entry_count=(value)
39
+ write_palette_value(8, value)
40
+ end
41
+
42
+ def reserved
43
+ palette_body.byteslice(12, 4).unpack1("V")
44
+ end
45
+
46
+ def reserved=(value)
47
+ write_palette_value(12, value)
48
+ end
49
+
50
+ private
51
+
52
+ def palette_body
53
+ bytes = body.to_s
54
+ raise ArgumentError, "invalid color correct palette body length" if bytes.bytesize < 16
55
+
56
+ bytes
57
+ end
58
+
59
+ def write_palette_value(offset, value)
60
+ bytes = palette_body.dup
61
+ raise ArgumentError, "palette value must be an Integer" unless value.is_a?(Integer)
62
+ raise RangeError, "palette value must fit an unsigned 32-bit integer" unless (0..0xffffffff).cover?(value)
63
+
64
+ bytes[offset, 4] = [value].pack("V")
65
+ self.body = bytes
66
+ end
13
67
  end
14
68
  end
15
69
  end
@@ -7,9 +7,84 @@ 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
+ # Color matching profile names and data retain their original byte spans.
11
11
  class ColorMatchToTargetW < Emf::Emr::Binary::Record
12
+ # Byte counts and opaque spans retain the original body layout.
12
13
  rest :body
14
+
15
+ def dw_action
16
+ profile_prefix.unpack1("V")
17
+ end
18
+
19
+ def dw_action=(value)
20
+ write_profile_scalar(0, value)
21
+ end
22
+
23
+ def dw_flags
24
+ profile_prefix.byteslice(4, 4).unpack1("V")
25
+ end
26
+
27
+ def dw_flags=(value)
28
+ write_profile_scalar(4, value)
29
+ end
30
+
31
+ def cb_name
32
+ profile_prefix.byteslice(8, 4).unpack1("V")
33
+ end
34
+
35
+ def cb_data
36
+ profile_prefix.byteslice(12, 4).unpack1("V")
37
+ end
38
+
39
+ def name_bytes
40
+ read_profile_bytes(16, cb_name)
41
+ end
42
+
43
+ def name_bytes=(value)
44
+ write_profile_bytes(16, cb_name, value)
45
+ end
46
+
47
+ def profile_bytes
48
+ read_profile_bytes(16 + cb_name, cb_data)
49
+ end
50
+
51
+ def profile_bytes=(value)
52
+ write_profile_bytes(16 + cb_name, cb_data, value)
53
+ end
54
+
55
+ private
56
+
57
+ def write_profile_scalar(offset, value)
58
+ raise ArgumentError, "color profile scalar must be an Integer" unless value.is_a?(Integer)
59
+ raise RangeError, "color profile scalar must fit an unsigned 32-bit integer" unless (0..0xFFFFFFFF).cover?(value)
60
+
61
+ write_profile_bytes(offset, 4, [value].pack("V"))
62
+ end
63
+
64
+ def profile_prefix
65
+ bytes = body.to_s
66
+ raise ArgumentError, "truncated color profile prefix" if bytes.bytesize < 16
67
+
68
+ bytes
69
+ end
70
+
71
+ def read_profile_bytes(offset, length)
72
+ bytes = profile_prefix
73
+ raise ArgumentError, "truncated color profile span" if offset + length > bytes.bytesize
74
+
75
+ bytes.byteslice(offset, length).b
76
+ end
77
+
78
+ def write_profile_bytes(offset, length, value)
79
+ read_profile_bytes(offset, length)
80
+ unless value.is_a?(String) && value.bytesize == length
81
+ raise ArgumentError, "color profile value must be a String of #{length} bytes"
82
+ end
83
+
84
+ bytes = body.to_s.b
85
+ bytes[offset, length] = value.b
86
+ self.body = bytes
87
+ end
13
88
  end
14
89
  end
15
90
  end
@@ -7,9 +7,75 @@ 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
+ # Logical color space fields retain their original fixed-width wire bytes.
11
11
  class CreateColorspace < Emf::Emr::Binary::Record
12
12
  rest :body
13
+
14
+ [
15
+ [:ih_cs, 0, "V", 4],
16
+ [:lcs_signature, 4, "V", 332],
17
+ [:lcs_version, 8, "V", 332],
18
+ [:lcs_size, 12, "V", 332],
19
+ [:color_space_type, 16, "l<", 332],
20
+ [:intent, 20, "l<", 332],
21
+ [:gamma_red, 60, "V", 332],
22
+ [:gamma_green, 64, "V", 332],
23
+ [:gamma_blue, 68, "V", 332]
24
+ ].each do |name, offset, format, required|
25
+ define_method(name) do
26
+ colorspace_body(required).byteslice(offset, 4).unpack1(format)
27
+ end
28
+
29
+ define_method("#{name}=") do |value|
30
+ write_colorspace_value(offset, value, format, required)
31
+ end
32
+ end
33
+
34
+ def endpoints_bytes
35
+ colorspace_body(332).byteslice(24, 36).b
36
+ end
37
+
38
+ def endpoints_bytes=(value)
39
+ replace_colorspace_bytes(24, 36, value, 332)
40
+ end
41
+
42
+ def filename_bytes
43
+ colorspace_body(332).byteslice(72, 260).b
44
+ end
45
+
46
+ def filename_bytes=(value)
47
+ replace_colorspace_bytes(72, 260, value, 332)
48
+ end
49
+
50
+ private
51
+
52
+ def colorspace_body(required)
53
+ bytes = body.to_s
54
+ raise ArgumentError, "truncated logical color space body" if bytes.bytesize < required
55
+
56
+ bytes
57
+ end
58
+
59
+ def write_colorspace_value(offset, value, format, required)
60
+ colorspace_body(required)
61
+ raise ArgumentError, "color space value must be an Integer" unless value.is_a?(Integer)
62
+
63
+ range = format == "l<" ? (-0x80000000..0x7fffffff) : (0..0xffffffff)
64
+ raise RangeError, "color space value must fit its 32-bit field" unless range.cover?(value)
65
+
66
+ replace_colorspace_bytes(offset, 4, [value].pack(format), required)
67
+ end
68
+
69
+ def replace_colorspace_bytes(offset, length, value, required)
70
+ bytes = colorspace_body(required)
71
+ unless value.is_a?(String) && value.bytesize == length
72
+ raise ArgumentError, "color space value must be a String of #{length} bytes"
73
+ end
74
+
75
+ replacement = bytes.b
76
+ replacement[offset, length] = value.b
77
+ self.body = replacement
78
+ end
13
79
  end
14
80
  end
15
81
  end