rmagick 2.13.2 → 4.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rmagick might be problematic. Click here for more details.

Files changed (380) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +63 -0
  3. data/.editorconfig +17 -0
  4. data/.github/ISSUE_TEMPLATE.md +17 -0
  5. data/.github/workflows/ci.yml +107 -0
  6. data/.gitignore +25 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +37 -0
  9. data/.rubocop_todo.yml +449 -0
  10. data/.yardopts +5 -0
  11. data/CHANGELOG.md +1277 -0
  12. data/CODE_OF_CONDUCT.md +128 -0
  13. data/CONTRIBUTING.md +81 -0
  14. data/Gemfile +4 -0
  15. data/LICENSE +20 -0
  16. data/README.md +324 -0
  17. data/Rakefile +190 -0
  18. data/before_install_linux.sh +69 -0
  19. data/before_install_osx.sh +57 -0
  20. data/deprecated/RMagick.rb +6 -0
  21. data/ext/RMagick/extconf.rb +334 -333
  22. data/ext/RMagick/rmagick.c +129 -127
  23. data/ext/RMagick/rmagick.h +221 -320
  24. data/ext/RMagick/rmdraw.c +458 -767
  25. data/ext/RMagick/rmenum.c +305 -752
  26. data/ext/RMagick/rmfill.c +231 -199
  27. data/ext/RMagick/rmilist.c +437 -478
  28. data/ext/RMagick/rmimage.c +6130 -5342
  29. data/ext/RMagick/rminfo.c +695 -833
  30. data/ext/RMagick/rmkinfo.c +198 -0
  31. data/ext/RMagick/rmmain.c +752 -506
  32. data/ext/RMagick/rmmontage.c +94 -152
  33. data/ext/RMagick/rmpixel.c +619 -425
  34. data/ext/RMagick/rmstruct.c +206 -309
  35. data/ext/RMagick/rmutil.c +514 -344
  36. data/lib/rmagick/version.rb +5 -0
  37. data/lib/rmagick.rb +1 -0
  38. data/lib/rmagick_internal.rb +1922 -0
  39. data/lib/rvg/clippath.rb +35 -39
  40. data/lib/rvg/container.rb +123 -124
  41. data/lib/rvg/deep_equal.rb +45 -49
  42. data/lib/rvg/describable.rb +41 -47
  43. data/lib/rvg/embellishable.rb +380 -411
  44. data/lib/rvg/misc.rb +691 -711
  45. data/lib/rvg/paint.rb +43 -47
  46. data/lib/rvg/pathdata.rb +119 -125
  47. data/lib/rvg/rvg.rb +214 -213
  48. data/lib/rvg/stretchable.rb +153 -162
  49. data/lib/rvg/stylable.rb +111 -117
  50. data/lib/rvg/text.rb +169 -180
  51. data/lib/rvg/transformable.rb +121 -127
  52. data/lib/rvg/units.rb +58 -61
  53. data/rmagick.gemspec +36 -16
  54. metadata +188 -365
  55. data/ChangeLog +0 -773
  56. data/Doxyfile +0 -1514
  57. data/README-Mac-OSX.txt +0 -1
  58. data/README.html +0 -10
  59. data/build_tarball.rake +0 -215
  60. data/doc/comtasks.html +0 -287
  61. data/doc/constants.html +0 -1581
  62. data/doc/css/doc.css +0 -299
  63. data/doc/css/popup.css +0 -34
  64. data/doc/css/ref.css +0 -67
  65. data/doc/draw.html +0 -3269
  66. data/doc/ex/InitialCoords.rb +0 -23
  67. data/doc/ex/NewCoordSys.rb +0 -32
  68. data/doc/ex/OrigCoordSys.rb +0 -18
  69. data/doc/ex/PreserveAspectRatio.rb +0 -205
  70. data/doc/ex/RotateScale.rb +0 -37
  71. data/doc/ex/Skew.rb +0 -38
  72. data/doc/ex/Use01.rb +0 -16
  73. data/doc/ex/Use02.rb +0 -21
  74. data/doc/ex/Use03.rb +0 -16
  75. data/doc/ex/ViewBox.rb +0 -33
  76. data/doc/ex/adaptive_threshold.rb +0 -10
  77. data/doc/ex/add_noise.rb +0 -17
  78. data/doc/ex/affine.rb +0 -48
  79. data/doc/ex/affine_transform.rb +0 -20
  80. data/doc/ex/arc.rb +0 -49
  81. data/doc/ex/arcpath.rb +0 -33
  82. data/doc/ex/arcs01.rb +0 -28
  83. data/doc/ex/arcs02.rb +0 -61
  84. data/doc/ex/average.rb +0 -15
  85. data/doc/ex/axes.rb +0 -64
  86. data/doc/ex/baseline_shift01.rb +0 -18
  87. data/doc/ex/bilevel_channel.rb +0 -9
  88. data/doc/ex/blur_image.rb +0 -12
  89. data/doc/ex/border.rb +0 -10
  90. data/doc/ex/bounding_box.rb +0 -44
  91. data/doc/ex/cbezier1.rb +0 -42
  92. data/doc/ex/cbezier2.rb +0 -42
  93. data/doc/ex/cbezier3.rb +0 -42
  94. data/doc/ex/cbezier4.rb +0 -43
  95. data/doc/ex/cbezier5.rb +0 -43
  96. data/doc/ex/cbezier6.rb +0 -53
  97. data/doc/ex/channel.rb +0 -26
  98. data/doc/ex/charcoal.rb +0 -12
  99. data/doc/ex/chop.rb +0 -29
  100. data/doc/ex/circle.rb +0 -33
  101. data/doc/ex/circle01.rb +0 -17
  102. data/doc/ex/clip_path.rb +0 -60
  103. data/doc/ex/coalesce.rb +0 -60
  104. data/doc/ex/color_fill_to_border.rb +0 -29
  105. data/doc/ex/color_floodfill.rb +0 -28
  106. data/doc/ex/color_histogram.rb +0 -48
  107. data/doc/ex/color_reset.rb +0 -11
  108. data/doc/ex/colorize.rb +0 -16
  109. data/doc/ex/colors.rb +0 -64
  110. data/doc/ex/compose_mask.rb +0 -23
  111. data/doc/ex/composite.rb +0 -135
  112. data/doc/ex/composite_layers.rb +0 -53
  113. data/doc/ex/composite_tiled.rb +0 -23
  114. data/doc/ex/contrast.rb +0 -36
  115. data/doc/ex/crop.rb +0 -31
  116. data/doc/ex/crop_with_gravity.rb +0 -46
  117. data/doc/ex/cubic01.rb +0 -45
  118. data/doc/ex/cubic02.rb +0 -94
  119. data/doc/ex/cycle_colormap.rb +0 -21
  120. data/doc/ex/dissolve.rb +0 -13
  121. data/doc/ex/drawcomp.rb +0 -42
  122. data/doc/ex/drop_shadow.rb +0 -60
  123. data/doc/ex/edge.rb +0 -11
  124. data/doc/ex/ellipse.rb +0 -45
  125. data/doc/ex/ellipse01.rb +0 -22
  126. data/doc/ex/emboss.rb +0 -11
  127. data/doc/ex/enhance.rb +0 -28
  128. data/doc/ex/equalize.rb +0 -11
  129. data/doc/ex/evenodd.rb +0 -43
  130. data/doc/ex/fill_pattern.rb +0 -26
  131. data/doc/ex/flatten_images.rb +0 -36
  132. data/doc/ex/flip.rb +0 -11
  133. data/doc/ex/flop.rb +0 -11
  134. data/doc/ex/font_styles.rb +0 -34
  135. data/doc/ex/fonts.rb +0 -20
  136. data/doc/ex/frame.rb +0 -12
  137. data/doc/ex/gaussian_blur.rb +0 -11
  138. data/doc/ex/get_multiline_type_metrics.rb +0 -42
  139. data/doc/ex/get_pixels.rb +0 -48
  140. data/doc/ex/get_type_metrics.rb +0 -146
  141. data/doc/ex/gradientfill.rb +0 -27
  142. data/doc/ex/grav.rb +0 -46
  143. data/doc/ex/gravity.rb +0 -79
  144. data/doc/ex/group.rb +0 -26
  145. data/doc/ex/hatchfill.rb +0 -27
  146. data/doc/ex/image.rb +0 -46
  147. data/doc/ex/images/Apple.miff +0 -0
  148. data/doc/ex/images/Ballerina.jpg +0 -0
  149. data/doc/ex/images/Ballerina3.jpg +0 -0
  150. data/doc/ex/images/Button_0.gif +0 -0
  151. data/doc/ex/images/Button_1.gif +0 -0
  152. data/doc/ex/images/Button_2.gif +0 -0
  153. data/doc/ex/images/Button_3.gif +0 -0
  154. data/doc/ex/images/Button_4.gif +0 -0
  155. data/doc/ex/images/Button_5.gif +0 -0
  156. data/doc/ex/images/Button_6.gif +0 -0
  157. data/doc/ex/images/Button_7.gif +0 -0
  158. data/doc/ex/images/Button_8.gif +0 -0
  159. data/doc/ex/images/Button_9.gif +0 -0
  160. data/doc/ex/images/Button_A.gif +0 -0
  161. data/doc/ex/images/Button_B.gif +0 -0
  162. data/doc/ex/images/Button_C.gif +0 -0
  163. data/doc/ex/images/Button_D.gif +0 -0
  164. data/doc/ex/images/Button_E.gif +0 -0
  165. data/doc/ex/images/Button_F.gif +0 -0
  166. data/doc/ex/images/Button_G.gif +0 -0
  167. data/doc/ex/images/Button_H.gif +0 -0
  168. data/doc/ex/images/Button_I.gif +0 -0
  169. data/doc/ex/images/Button_J.gif +0 -0
  170. data/doc/ex/images/Button_K.gif +0 -0
  171. data/doc/ex/images/Button_L.gif +0 -0
  172. data/doc/ex/images/Button_M.gif +0 -0
  173. data/doc/ex/images/Button_N.gif +0 -0
  174. data/doc/ex/images/Button_O.gif +0 -0
  175. data/doc/ex/images/Button_P.gif +0 -0
  176. data/doc/ex/images/Button_Q.gif +0 -0
  177. data/doc/ex/images/Button_R.gif +0 -0
  178. data/doc/ex/images/Button_S.gif +0 -0
  179. data/doc/ex/images/Button_T.gif +0 -0
  180. data/doc/ex/images/Button_U.gif +0 -0
  181. data/doc/ex/images/Button_V.gif +0 -0
  182. data/doc/ex/images/Button_W.gif +0 -0
  183. data/doc/ex/images/Button_X.gif +0 -0
  184. data/doc/ex/images/Button_Y.gif +0 -0
  185. data/doc/ex/images/Button_Z.gif +0 -0
  186. data/doc/ex/images/Cheetah.jpg +0 -0
  187. data/doc/ex/images/Coffee.wmf +0 -0
  188. data/doc/ex/images/Flower_Hat.jpg +0 -0
  189. data/doc/ex/images/Gold_Statue.jpg +0 -0
  190. data/doc/ex/images/Hot_Air_Balloons.jpg +0 -0
  191. data/doc/ex/images/Hot_Air_Balloons_H.jpg +0 -0
  192. data/doc/ex/images/Leaf.miff +0 -0
  193. data/doc/ex/images/No.wmf +0 -0
  194. data/doc/ex/images/Polynesia.jpg +0 -0
  195. data/doc/ex/images/Red_Rocks.jpg +0 -0
  196. data/doc/ex/images/Rocks_On_Beach.miff +0 -0
  197. data/doc/ex/images/Shorts.jpg +0 -0
  198. data/doc/ex/images/Snake.wmf +0 -0
  199. data/doc/ex/images/Violin.jpg +0 -0
  200. data/doc/ex/images/Yellow_Rose.miff +0 -0
  201. data/doc/ex/images/big-duck.gif +0 -0
  202. data/doc/ex/images/duck.gif +0 -0
  203. data/doc/ex/images/duck0.gif +0 -0
  204. data/doc/ex/images/duck1.gif +0 -0
  205. data/doc/ex/images/duck10.gif +0 -0
  206. data/doc/ex/images/duck11.gif +0 -0
  207. data/doc/ex/images/duck12.gif +0 -0
  208. data/doc/ex/images/duck13.gif +0 -0
  209. data/doc/ex/images/duck14.gif +0 -0
  210. data/doc/ex/images/duck15.gif +0 -0
  211. data/doc/ex/images/duck2.gif +0 -0
  212. data/doc/ex/images/duck3.gif +0 -0
  213. data/doc/ex/images/duck4.gif +0 -0
  214. data/doc/ex/images/duck5.gif +0 -0
  215. data/doc/ex/images/duck6.gif +0 -0
  216. data/doc/ex/images/duck7.gif +0 -0
  217. data/doc/ex/images/duck8.gif +0 -0
  218. data/doc/ex/images/duck9.gif +0 -0
  219. data/doc/ex/images/graydient230x6.gif +0 -0
  220. data/doc/ex/images/logo400x83.gif +0 -0
  221. data/doc/ex/images/model.miff +0 -0
  222. data/doc/ex/images/notimplemented.gif +0 -0
  223. data/doc/ex/images/smile.miff +0 -0
  224. data/doc/ex/images/spin.gif +0 -0
  225. data/doc/ex/implode.rb +0 -34
  226. data/doc/ex/level.rb +0 -11
  227. data/doc/ex/level_colors.rb +0 -11
  228. data/doc/ex/line.rb +0 -42
  229. data/doc/ex/line01.rb +0 -23
  230. data/doc/ex/mask.rb +0 -36
  231. data/doc/ex/matte_fill_to_border.rb +0 -40
  232. data/doc/ex/matte_floodfill.rb +0 -33
  233. data/doc/ex/matte_replace.rb +0 -40
  234. data/doc/ex/median_filter.rb +0 -28
  235. data/doc/ex/modulate.rb +0 -11
  236. data/doc/ex/mono.rb +0 -23
  237. data/doc/ex/morph.rb +0 -26
  238. data/doc/ex/mosaic.rb +0 -35
  239. data/doc/ex/motion_blur.rb +0 -11
  240. data/doc/ex/negate.rb +0 -11
  241. data/doc/ex/negate_channel.rb +0 -9
  242. data/doc/ex/nested_rvg.rb +0 -21
  243. data/doc/ex/nonzero.rb +0 -43
  244. data/doc/ex/normalize.rb +0 -11
  245. data/doc/ex/oil_paint.rb +0 -11
  246. data/doc/ex/opacity.rb +0 -37
  247. data/doc/ex/ordered_dither.rb +0 -11
  248. data/doc/ex/path.rb +0 -64
  249. data/doc/ex/pattern1.rb +0 -25
  250. data/doc/ex/pattern2.rb +0 -26
  251. data/doc/ex/polaroid.rb +0 -28
  252. data/doc/ex/polygon.rb +0 -24
  253. data/doc/ex/polygon01.rb +0 -23
  254. data/doc/ex/polyline.rb +0 -23
  255. data/doc/ex/polyline01.rb +0 -23
  256. data/doc/ex/posterize.rb +0 -8
  257. data/doc/ex/preview.rb +0 -9
  258. data/doc/ex/qbezierpath.rb +0 -52
  259. data/doc/ex/quad01.rb +0 -36
  260. data/doc/ex/quantize-m.rb +0 -25
  261. data/doc/ex/radial_blur.rb +0 -9
  262. data/doc/ex/raise.rb +0 -8
  263. data/doc/ex/random_threshold_channel.rb +0 -13
  264. data/doc/ex/rect01.rb +0 -15
  265. data/doc/ex/rect02.rb +0 -22
  266. data/doc/ex/rectangle.rb +0 -35
  267. data/doc/ex/reduce_noise.rb +0 -28
  268. data/doc/ex/remap.rb +0 -12
  269. data/doc/ex/remap_images.rb +0 -21
  270. data/doc/ex/resize_to_fill.rb +0 -10
  271. data/doc/ex/resize_to_fit.rb +0 -10
  272. data/doc/ex/roll.rb +0 -9
  273. data/doc/ex/rotate.rb +0 -45
  274. data/doc/ex/rotate_f.rb +0 -14
  275. data/doc/ex/roundrect.rb +0 -34
  276. data/doc/ex/rubyname.rb +0 -30
  277. data/doc/ex/rvg_clippath.rb +0 -14
  278. data/doc/ex/rvg_linecap.rb +0 -43
  279. data/doc/ex/rvg_linejoin.rb +0 -41
  280. data/doc/ex/rvg_opacity.rb +0 -19
  281. data/doc/ex/rvg_pattern.rb +0 -26
  282. data/doc/ex/rvg_stroke_dasharray.rb +0 -12
  283. data/doc/ex/segment.rb +0 -11
  284. data/doc/ex/sepiatone.rb +0 -8
  285. data/doc/ex/shade.rb +0 -11
  286. data/doc/ex/shadow.rb +0 -31
  287. data/doc/ex/shave.rb +0 -15
  288. data/doc/ex/shear.rb +0 -10
  289. data/doc/ex/sketch.rb +0 -18
  290. data/doc/ex/skewx.rb +0 -52
  291. data/doc/ex/skewy.rb +0 -47
  292. data/doc/ex/smile.rb +0 -125
  293. data/doc/ex/solarize.rb +0 -11
  294. data/doc/ex/sparse_color.rb +0 -55
  295. data/doc/ex/splice.rb +0 -9
  296. data/doc/ex/spread.rb +0 -11
  297. data/doc/ex/stegano.rb +0 -55
  298. data/doc/ex/stroke_dasharray.rb +0 -43
  299. data/doc/ex/stroke_fill.rb +0 -11
  300. data/doc/ex/stroke_linecap.rb +0 -44
  301. data/doc/ex/stroke_linejoin.rb +0 -48
  302. data/doc/ex/stroke_width.rb +0 -49
  303. data/doc/ex/swirl.rb +0 -17
  304. data/doc/ex/text.rb +0 -37
  305. data/doc/ex/text01.rb +0 -17
  306. data/doc/ex/text_align.rb +0 -36
  307. data/doc/ex/text_antialias.rb +0 -38
  308. data/doc/ex/text_styles.rb +0 -21
  309. data/doc/ex/text_undercolor.rb +0 -28
  310. data/doc/ex/texture_fill_to_border.rb +0 -34
  311. data/doc/ex/texture_floodfill.rb +0 -32
  312. data/doc/ex/texturefill.rb +0 -25
  313. data/doc/ex/threshold.rb +0 -13
  314. data/doc/ex/to_blob.rb +0 -14
  315. data/doc/ex/translate.rb +0 -39
  316. data/doc/ex/transparent.rb +0 -38
  317. data/doc/ex/transpose.rb +0 -9
  318. data/doc/ex/transverse.rb +0 -9
  319. data/doc/ex/tref01.rb +0 -25
  320. data/doc/ex/triangle01.rb +0 -16
  321. data/doc/ex/trim.rb +0 -24
  322. data/doc/ex/tspan01.rb +0 -18
  323. data/doc/ex/tspan02.rb +0 -19
  324. data/doc/ex/tspan03.rb +0 -21
  325. data/doc/ex/unsharp_mask.rb +0 -28
  326. data/doc/ex/viewex.rb +0 -35
  327. data/doc/ex/vignette.rb +0 -12
  328. data/doc/ex/watermark.rb +0 -28
  329. data/doc/ex/wave.rb +0 -9
  330. data/doc/ex/wet_floor.rb +0 -59
  331. data/doc/ex/writing_mode01.rb +0 -27
  332. data/doc/ex/writing_mode02.rb +0 -26
  333. data/doc/ilist.html +0 -2056
  334. data/doc/image1.html +0 -4680
  335. data/doc/image2.html +0 -3665
  336. data/doc/image3.html +0 -4522
  337. data/doc/imageattrs.html +0 -1638
  338. data/doc/imusage.html +0 -514
  339. data/doc/index.html +0 -416
  340. data/doc/info.html +0 -1499
  341. data/doc/magick.html +0 -565
  342. data/doc/optequiv.html +0 -2435
  343. data/doc/rvg.html +0 -975
  344. data/doc/rvgclip.html +0 -248
  345. data/doc/rvggroup.html +0 -305
  346. data/doc/rvgimage.html +0 -289
  347. data/doc/rvgpattern.html +0 -475
  348. data/doc/rvgshape.html +0 -406
  349. data/doc/rvgstyle.html +0 -270
  350. data/doc/rvgtext.html +0 -465
  351. data/doc/rvgtspan.html +0 -238
  352. data/doc/rvgtut.html +0 -530
  353. data/doc/rvguse.html +0 -145
  354. data/doc/rvgxform.html +0 -294
  355. data/doc/scripts/doc.js +0 -22
  356. data/doc/scripts/stripeTables.js +0 -23
  357. data/doc/struct.html +0 -1339
  358. data/doc/usage.html +0 -1621
  359. data/examples/constitute.rb +0 -7
  360. data/examples/crop_with_gravity.rb +0 -46
  361. data/examples/demo.rb +0 -324
  362. data/examples/describe.rb +0 -44
  363. data/examples/find_similar_region.rb +0 -34
  364. data/examples/histogram.rb +0 -325
  365. data/examples/identify.rb +0 -187
  366. data/examples/image_opacity.rb +0 -29
  367. data/examples/import_export.rb +0 -31
  368. data/examples/pattern_fill.rb +0 -38
  369. data/examples/rotating_text.rb +0 -45
  370. data/examples/spinner.rb +0 -50
  371. data/examples/thumbnail.rb +0 -65
  372. data/examples/vignette.rb +0 -79
  373. data/ext/RMagick/MANIFEST +0 -358
  374. data/lib/RMagick.rb +0 -1962
  375. data/metaconfig +0 -7
  376. data/post-clean.rb +0 -12
  377. data/post-install.rb +0 -50
  378. data/post-setup.rb +0 -254
  379. data/setup.rb +0 -1585
  380. data/uninstall.rb +0 -76
data/lib/rvg/misc.rb CHANGED
@@ -1,740 +1,720 @@
1
1
  # $Id: misc.rb,v 1.17 2010/03/21 01:43:01 baror Exp $
2
2
  # Copyright (C) 2009 Timothy P. Hunter
3
3
  module Magick
4
- class RVG
5
-
6
- # This is a standard deep_copy method that is used in most classes.
7
- # Thanks to Robert Klemme.
8
- module Duplicatable
9
-
10
- def deep_copy(h = {})
11
- # Prevent recursion. If we reach the
12
- # object we started with, stop copying.
13
- copy = h[__id__]
14
- unless copy
15
- h[__id__] = copy = self.class.allocate
16
- ivars = instance_variables
17
- ivars.each do |ivar|
18
- ivalue = instance_variable_get(ivar)
19
- cvalue = case
20
- when NilClass === ivalue, Symbol === ivalue, Float === ivalue,
21
- Fixnum === ivalue, FalseClass === ivalue, TrueClass === ivalue
22
- ivalue
23
- when ivalue.respond_to?(:deep_copy)
24
- ivalue.deep_copy(h)
25
- when ivalue.respond_to?(:dup)
26
- ivalue.dup
27
- else
28
- ivalue
29
- end
30
- copy.instance_variable_set(ivar, cvalue)
31
- end
32
- copy.freeze if frozen?
33
- end
34
- return copy
35
- end
4
+ class RVG
5
+ # This is a standard deep_copy method that is used in most classes.
6
+ # Thanks to Robert Klemme.
7
+ module Duplicatable
8
+ def deep_copy(h = {})
9
+ # Prevent recursion. If we reach the
10
+ # object we started with, stop copying.
11
+ copy = h[__id__]
12
+ unless copy
13
+ h[__id__] = copy = self.class.allocate
14
+ ivars = instance_variables
15
+ ivars.each do |ivar|
16
+ ivalue = instance_variable_get(ivar)
17
+ cvalue = if ivalue.is_a?(NilClass) || ivalue.is_a?(Symbol) || ivalue.is_a?(Float) || ivalue.is_a?(Integer) || ivalue.is_a?(FalseClass) || ivalue.is_a?(TrueClass)
18
+ ivalue
19
+ elsif ivalue.respond_to?(:deep_copy)
20
+ ivalue.deep_copy(h)
21
+ elsif ivalue.respond_to?(:dup)
22
+ ivalue.dup
23
+ else
24
+ ivalue
25
+ end
26
+ copy.instance_variable_set(ivar, cvalue)
27
+ end
28
+ copy.freeze if frozen?
29
+ end
30
+ copy
31
+ end
32
+ end # module Duplicatable
33
+
34
+ # Convert an array of method arguments to Float objects. If any
35
+ # cannot be converted, raise ArgumentError and issue a message.
36
+ def self.fmsg(*args)
37
+ "at least one argument cannot be converted to Float (got #{args.map(&:class).join(', ')})"
38
+ end
39
+
40
+ def self.convert_to_float(*args)
41
+ allow_nil = false
42
+ if args.last == :allow_nil
43
+ allow_nil = true
44
+ args.pop
45
+ end
46
+ begin
47
+ fargs = args.map { |a| allow_nil && a.nil? ? a : Float(a) }
48
+ rescue ArgumentError, TypeError
49
+ raise ArgumentError, fmsg(*args)
50
+ end
51
+ fargs
52
+ end
53
+
54
+ def self.convert_one_to_float(arg)
55
+ begin
56
+ farg = Float(arg)
57
+ rescue ArgumentError, TypeError
58
+ raise ArgumentError, "argument cannot be converted to Float (got #{arg.class})"
59
+ end
60
+ farg
61
+ end
62
+ end # class RVG
63
+ end # module Magick
36
64
 
37
- end # module Duplicatable
65
+ module Magick
66
+ class RVG
67
+ class Utility
68
+ class TextStrategy
69
+ def initialize(context)
70
+ @ctx = context
71
+ @ctx.shadow.affine = @ctx.text_attrs.affine
72
+ end
38
73
 
74
+ def enquote(text)
75
+ return text if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
76
+
77
+ if !text['\'']
78
+ text = '\'' + text + '\''
79
+ return text
80
+ elsif !text['"']
81
+ text = '"' + text + '"'
82
+ return text
83
+ elsif !(text['{'] || text['}'])
84
+ text = '{' + text + '}'
85
+ return text
86
+ end
87
+
88
+ # escape existing braces, surround with braces
89
+ text.gsub!(/[}]/) { |b| '\\' + b }
90
+ '{' + text + '}'
91
+ end
39
92
 
40
- # Convert an array of method arguments to Float objects. If any
41
- # cannot be converted, raise ArgumentError and issue a message.
42
- def self.fmsg(*args)
43
- "at least one argument cannot be converted to Float (got #{args.collect {|a| a.class}.join(', ')})"
93
+ def glyph_metrics(glyph_orientation, glyph)
94
+ gm = @ctx.shadow.get_type_metrics('a' + glyph + 'a')
95
+ gm2 = @ctx.shadow.get_type_metrics('aa')
96
+ h = (gm.ascent - gm.descent + 0.5).to_i
97
+ w = gm.width - gm2.width
98
+ if glyph_orientation.zero? || glyph_orientation == 180
99
+ [w, h]
100
+ else
101
+ [h, w]
102
+ end
44
103
  end
45
104
 
46
- def self.convert_to_float(*args)
47
- allow_nil = false
48
- if args.last == :allow_nil
49
- allow_nil = true
50
- args.pop
105
+ def text_rel_coords(text)
106
+ y_rel_coords = []
107
+ x_rel_coords = []
108
+ first_word = true
109
+ words = text.split(::Magick::RVG::WORD_SEP)
110
+ words.each do |word|
111
+ unless first_word
112
+ wx, wy = get_word_spacing
113
+ x_rel_coords << wx
114
+ y_rel_coords << wy
51
115
  end
52
- begin
53
- fargs = args.collect { |a| (allow_nil && a.nil?) ? a : Float(a) }
54
- rescue ArgumentError, TypeError
55
- raise ArgumentError, self.fmsg(*args)
116
+ first_word = false
117
+ word.split('').each do |glyph|
118
+ wx, wy = get_letter_spacing(glyph)
119
+ x_rel_coords << wx
120
+ y_rel_coords << wy
56
121
  end
57
- return fargs
122
+ end
123
+ [x_rel_coords, y_rel_coords]
124
+ end
125
+
126
+ def shift_baseline(glyph_orientation, glyph)
127
+ glyph_dimensions = @ctx.shadow.get_type_metrics(glyph)
128
+ x = if glyph_orientation.zero? || glyph_orientation == 180
129
+ glyph_dimensions.width
130
+ else
131
+ glyph_dimensions.ascent - glyph_dimensions.descent
132
+ end
133
+ case @ctx.text_attrs.baseline_shift
134
+ when :baseline
135
+ x = 0
136
+ when :sub
137
+
138
+ when :super
139
+ x = -x
140
+ when /[-+]?(\d+)%/
141
+ m = Regexp.last_match(1) == '-' ? -1.0 : 1.0
142
+ x = (m * x * Regexp.last_match(1).to_f / 100.0)
143
+ else
144
+ x = -@ctx.text_attrs.baseline_shift
145
+ end
146
+ x
58
147
  end
59
148
 
60
- def self.convert_one_to_float(arg)
61
- begin
62
- farg = Float(arg)
63
- rescue ArgumentError, TypeError
64
- raise ArgumentError, "argument cannot be converted to Float (got #{arg.class})"
149
+ def render_glyph(glyph_orientation, x, y, glyph)
150
+ if glyph_orientation.zero?
151
+ @ctx.gc.text(x, y, enquote(glyph))
152
+ else
153
+ @ctx.gc.push
154
+ @ctx.gc.translate(x, y)
155
+ @ctx.gc.rotate(glyph_orientation)
156
+ @ctx.gc.translate(-x, -y)
157
+ @ctx.gc.text(x, y, enquote(glyph))
158
+ @ctx.gc.pop
159
+ end
160
+ end
161
+ end # class TextStrategy
162
+
163
+ class LRTextStrategy < TextStrategy
164
+ def get_word_spacing
165
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, ' ')[0]
166
+ [@word_space + @ctx.text_attrs.word_spacing, 0]
167
+ end
168
+
169
+ def get_letter_spacing(glyph)
170
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, glyph)
171
+ [gx + @ctx.text_attrs.letter_spacing, gy]
172
+ end
173
+
174
+ def render(x, y, text)
175
+ x_rel_coords, y_rel_coords = text_rel_coords(text)
176
+ dx = x_rel_coords.reduce(0) { |sum, a| sum + a }
177
+ dy = y_rel_coords.max
178
+
179
+ # We're handling the anchoring.
180
+ @ctx.gc.push
181
+ @ctx.gc.text_anchor(Magick::StartAnchor)
182
+ if @ctx.text_attrs.text_anchor == :end
183
+ x -= dx
184
+ elsif @ctx.text_attrs.text_anchor == :middle
185
+ x -= dx / 2
186
+ end
187
+
188
+ # Align the first glyph
189
+ case @ctx.text_attrs.glyph_orientation_horizontal
190
+ when 90
191
+ y -= dy
192
+ when 180
193
+ x += x_rel_coords.shift
194
+ x_rel_coords << 0
195
+ y -= dy
196
+ when 270
197
+ x += x_rel_coords[0]
198
+ end
199
+
200
+ y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0, 1])
201
+
202
+ first_word = true
203
+ text.split(::Magick::RVG::WORD_SEP).each do |word|
204
+ x += x_rel_coords.shift unless first_word
205
+ first_word = false
206
+ word.split('').each do |glyph|
207
+ render_glyph(@ctx.text_attrs.glyph_orientation_horizontal, x, y, glyph)
208
+ x += x_rel_coords.shift
65
209
  end
66
- return farg
210
+ end
211
+
212
+ @ctx.gc.pop
213
+ [dx, 0]
67
214
  end
215
+ end # class LRTextStrategy
68
216
 
69
- end # class RVG
70
- end # module Magick
217
+ class RLTextStrategy < TextStrategy
218
+ def render(_x, _y, _text)
219
+ raise NotImplementedError
220
+ end
221
+ end # class RLTextStrategy
71
222
 
223
+ class TBTextStrategy < TextStrategy
224
+ def get_word_spacing
225
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, ' ')[1]
226
+ [0, @word_space + @ctx.text_attrs.word_spacing]
227
+ end
72
228
 
229
+ def get_letter_spacing(glyph)
230
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, glyph)
231
+ [gx, gy + @ctx.text_attrs.letter_spacing]
232
+ end
73
233
 
234
+ def render(x, y, text)
235
+ x_rel_coords, y_rel_coords = text_rel_coords(text)
236
+ dx = x_rel_coords.max
237
+ dy = y_rel_coords.reduce(0) { |sum, a| sum + a }
238
+
239
+ # We're handling the anchoring.
240
+ @ctx.gc.push
241
+ @ctx.gc.text_anchor(Magick::StartAnchor)
242
+ if @ctx.text_attrs.text_anchor == :end
243
+ y -= dy
244
+ elsif @ctx.text_attrs.text_anchor == :middle
245
+ y -= dy / 2
246
+ end
247
+
248
+ # Align the first glyph such that its center
249
+ # is aligned on x and its top is aligned on y.
250
+
251
+ case @ctx.text_attrs.glyph_orientation_vertical
252
+ when 0
253
+ x -= x_rel_coords.max / 2
254
+ y += y_rel_coords[0]
255
+ when 90
256
+ x -= x_rel_coords.max / 2
257
+ when 180
258
+ x += x_rel_coords.max / 2
259
+ when 270
260
+ x += x_rel_coords.max / 2
261
+ y += y_rel_coords.shift
262
+ y_rel_coords << 0 # since we used an element we need to add a dummy
263
+ end
264
+
265
+ x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0, 1])
266
+
267
+ first_word = true
268
+ text.split(::Magick::RVG::WORD_SEP).each do |word|
269
+ unless first_word
270
+ y += y_rel_coords.shift
271
+ x_rel_coords.shift
272
+ end
273
+ first_word = false
274
+ word.split('').each do |glyph|
275
+ case @ctx.text_attrs.glyph_orientation_vertical.to_i
276
+ when 0, 90, 270
277
+ x_shift = (dx - x_rel_coords.shift) / 2
278
+ when 180
279
+ x_shift = -(dx - x_rel_coords.shift) / 2
280
+ end
281
+
282
+ render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x + x_shift, y, glyph)
283
+ y += y_rel_coords.shift
284
+ end
285
+ end
74
286
 
287
+ @ctx.gc.pop
288
+ [0, dy]
289
+ end
290
+ end # class TBTextStrategy
291
+
292
+ # Handle "easy" text
293
+ class DefaultTextStrategy < TextStrategy
294
+ def render(x, y, text)
295
+ @ctx.gc.text(x, y, enquote(text))
296
+ tm = @ctx.shadow.get_type_metrics(text)
297
+ dx = case @ctx.text_attrs.text_anchor
298
+ when :start
299
+ tm.width
300
+ when :middle
301
+ tm.width / 2
302
+ when :end
303
+ 0
304
+ end
305
+ [dx, 0]
306
+ end
307
+ end # class NormalTextStrategy
308
+ end # class Utility
309
+ end # class RVG
310
+ end # module Magick
75
311
 
76
312
  module Magick
77
- class RVG
78
- class Utility
79
-
80
- class TextStrategy
81
-
82
- def initialize(context)
83
- @ctx = context
84
- @ctx.shadow.affine = @ctx.text_attrs.affine
85
- end
86
-
87
- def enquote(text)
88
- if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
89
- return text
90
- elsif !text['\'']
91
- text = '\''+text+'\''
92
- return text
93
- elsif !text['"']
94
- text = '"'+text+'"'
95
- return text
96
- elsif !(text['{'] || text['}'])
97
- text = '{'+text+'}'
98
- return text
99
- end
100
-
101
- # escape existing braces, surround with braces
102
- text.gsub!(/[}]/) { |b| '\\' + b }
103
- return '{' + text + '}'
104
- end
105
-
106
- def glyph_metrics(glyph_orientation, glyph)
107
- gm = @ctx.shadow.get_type_metrics("a" + glyph + "a")
108
- gm2 = @ctx.shadow.get_type_metrics("aa")
109
- h = (gm.ascent - gm.descent + 0.5 ).to_i
110
- w = gm.width - gm2.width
111
- if glyph_orientation == 0 || glyph_orientation == 180
112
- [w, h]
113
- else
114
- [h, w]
115
- end
116
- end
117
-
118
- def text_rel_coords(text)
119
- y_rel_coords = []
120
- x_rel_coords = []
121
- first_word = true
122
- words = text.split(::Magick::RVG::WORD_SEP)
123
- words.each do |word|
124
- unless first_word
125
- wx, wy = get_word_spacing()
126
- x_rel_coords << wx
127
- y_rel_coords << wy
128
- end
129
- first_word = false
130
- word.split('').each do |glyph|
131
- wx, wy = get_letter_spacing(glyph)
132
- x_rel_coords << wx
133
- y_rel_coords << wy
134
- end
135
- end
136
- [x_rel_coords, y_rel_coords]
137
- end
138
-
139
- def shift_baseline(glyph_orientation, glyph)
140
- glyph_dimensions = @ctx.shadow.get_type_metrics(glyph)
141
- if glyph_orientation == 0 || glyph_orientation == 180
142
- x = glyph_dimensions.width
143
- else
144
- x = glyph_dimensions.ascent - glyph_dimensions.descent
145
- end
146
- case @ctx.text_attrs.baseline_shift
147
- when :baseline
148
- x = 0
149
- when :sub
150
- ;
151
- when :super
152
- x = -x
153
- when /[-+]?(\d+)%/
154
- m = $1 == '-' ? -1.0 : 1.0
155
- x = (m * x * $1.to_f / 100.0)
156
- else
157
- x = -@ctx.text_attrs.baseline_shift
158
- end
159
- return x
160
- end
161
-
162
- def render_glyph(glyph_orientation, x, y, glyph)
163
- if glyph_orientation == 0
164
- @ctx.gc.text(x, y, enquote(glyph))
165
- else
166
- @ctx.gc.push
167
- @ctx.gc.translate(x, y)
168
- @ctx.gc.rotate(glyph_orientation)
169
- @ctx.gc.translate(-x, -y)
170
- @ctx.gc.text(x, y, enquote(glyph))
171
- @ctx.gc.pop
172
- end
173
- end
174
-
175
- end # class TextStrategy
176
-
177
- class LRTextStrategy < TextStrategy
178
-
179
- def get_word_spacing()
180
- @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, ' ')[0]
181
- [@word_space + @ctx.text_attrs.word_spacing, 0]
182
- end
183
-
184
- def get_letter_spacing(glyph)
185
- gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, glyph)
186
- [gx+@ctx.text_attrs.letter_spacing, gy]
187
- end
188
-
189
- def render(x, y, text)
190
- x_rel_coords, y_rel_coords = text_rel_coords(text)
191
- dx = x_rel_coords.inject(0) {|sum, a| sum + a}
192
- dy = y_rel_coords.max
193
-
194
- # We're handling the anchoring.
195
- @ctx.gc.push()
196
- @ctx.gc.text_anchor(Magick::StartAnchor)
197
- if @ctx.text_attrs.text_anchor == :end
198
- x -= dx
199
- elsif @ctx.text_attrs.text_anchor == :middle
200
- x -= dx / 2
201
- end
202
-
203
- # Align the first glyph
204
- case @ctx.text_attrs.glyph_orientation_horizontal
205
- when 0
206
- ;
207
- when 90
208
- y -= dy
209
- when 180
210
- x += x_rel_coords.shift
211
- x_rel_coords << 0
212
- y -= dy
213
- when 270
214
- x += x_rel_coords[0]
215
- end
216
-
217
- y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0,1])
218
-
219
- first_word = true
220
- text.split(::Magick::RVG::WORD_SEP).each do |word|
221
- unless first_word
222
- x += x_rel_coords.shift
223
- end
224
- first_word = false
225
- word.split('').each do |glyph|
226
- render_glyph(@ctx.text_attrs.glyph_orientation_horizontal, x, y, glyph)
227
- x += x_rel_coords.shift
228
- end
229
- end
230
-
231
- @ctx.gc.pop()
232
- [dx, 0]
233
- end
234
-
235
- end # class LRTextStrategy
236
-
237
- class RLTextStrategy < TextStrategy
238
-
239
- def render(x, y, text)
240
- raise NotImplementedError
241
- end
242
-
243
- end # class RLTextStrategy
244
-
245
-
246
- class TBTextStrategy < TextStrategy
247
-
248
- def get_word_spacing()
249
- @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, ' ')[1]
250
- [0, @word_space + @ctx.text_attrs.word_spacing]
251
- end
252
-
253
- def get_letter_spacing(glyph)
254
- gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, glyph)
255
- [gx, gy+@ctx.text_attrs.letter_spacing]
256
- end
257
-
258
- def render(x, y, text)
259
- x_rel_coords, y_rel_coords = text_rel_coords(text)
260
- dx = x_rel_coords.max
261
- dy = y_rel_coords.inject(0) {|sum, a| sum + a}
262
-
263
- # We're handling the anchoring.
264
- @ctx.gc.push()
265
- @ctx.gc.text_anchor(Magick::StartAnchor)
266
- if @ctx.text_attrs.text_anchor == :end
267
- y -= dy
268
- elsif @ctx.text_attrs.text_anchor == :middle
269
- y -= dy / 2
270
- end
271
-
272
- # Align the first glyph such that its center
273
- # is aligned on x and its top is aligned on y.
274
-
275
- case @ctx.text_attrs.glyph_orientation_vertical
276
- when 0
277
- x -= x_rel_coords.max / 2
278
- y += y_rel_coords[0]
279
- when 90
280
- x -= x_rel_coords.max / 2
281
- when 180
282
- x += x_rel_coords.max / 2
283
- when 270
284
- x += x_rel_coords.max / 2
285
- y += y_rel_coords.shift
286
- y_rel_coords << 0 # since we used an element we need to add a dummy
287
- end
288
-
289
- x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0,1])
290
-
291
- first_word = true
292
- text.split(::Magick::RVG::WORD_SEP).each do |word|
293
- unless first_word
294
- y += y_rel_coords.shift
295
- x_rel_coords.shift
296
- end
297
- first_word = false
298
- word.split('').each do |glyph|
299
- case @ctx.text_attrs.glyph_orientation_vertical.to_i
300
- when 0, 90, 270
301
- x_shift = (dx - x_rel_coords.shift) / 2
302
- when 180
303
- x_shift = -(dx - x_rel_coords.shift) / 2
304
- end
305
-
306
- render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x+x_shift, y, glyph)
307
- y += y_rel_coords.shift
308
- end
309
- end
310
-
311
- @ctx.gc.pop()
312
- [0, dy]
313
- end
314
-
315
- end # class TBTextStrategy
316
-
317
- # Handle "easy" text
318
- class DefaultTextStrategy < TextStrategy
319
-
320
- def render(x, y, text)
321
- @ctx.gc.text(x, y, enquote(text))
322
- tm = @ctx.shadow.get_type_metrics(text)
323
- dx = case @ctx.text_attrs.text_anchor
324
- when :start
325
- tm.width
326
- when :middle
327
- tm.width / 2
328
- when :end
329
- 0
313
+ class RVG
314
+ class Utility
315
+ class TextAttributes
316
+ WRITING_MODE = %w[lr-tb lr rl-tb rl tb-rl tb]
317
+
318
+ def initialize
319
+ @affine = []
320
+ @affine << Magick::AffineMatrix.new(1, 0, 0, 1, 0, 0)
321
+ @baseline_shift = []
322
+ @baseline_shift << :baseline
323
+ @glyph_orientation_horizontal = []
324
+ @glyph_orientation_horizontal << 0
325
+ @glyph_orientation_vertical = []
326
+ @glyph_orientation_vertical << 90
327
+ @letter_spacing = []
328
+ @letter_spacing << 0
329
+ @text_anchor = []
330
+ @text_anchor << :start
331
+ @word_spacing = []
332
+ @word_spacing << 0
333
+ @writing_mode = []
334
+ @writing_mode << 'lr-tb'
335
+ end
336
+
337
+ def push
338
+ @affine.push(@affine.last.dup)
339
+ @baseline_shift.push(@baseline_shift.last)
340
+ @text_anchor.push(@text_anchor.last)
341
+ @writing_mode.push(@writing_mode.last.dup)
342
+ @glyph_orientation_vertical.push(@glyph_orientation_vertical.last)
343
+ @glyph_orientation_horizontal.push(@glyph_orientation_horizontal.last)
344
+ @letter_spacing.push(@letter_spacing.last)
345
+ @word_spacing.push(@word_spacing.last)
346
+ end
347
+
348
+ def pop
349
+ @affine.pop
350
+ @baseline_shift.pop
351
+ @text_anchor.pop
352
+ @writing_mode.pop
353
+ @glyph_orientation_vertical.pop
354
+ @glyph_orientation_horizontal.pop
355
+ @letter_spacing.pop
356
+ @word_spacing.pop
357
+ end
358
+
359
+ def set_affine(sx, rx, ry, sy, tx, ty)
360
+ @affine[-1].sx = sx
361
+ @affine[-1].rx = rx
362
+ @affine[-1].ry = ry
363
+ @affine[-1].sy = sy
364
+ @affine[-1].tx = tx
365
+ @affine[-1].ty = ty
366
+ end
367
+
368
+ def affine
369
+ @affine[-1]
370
+ end
371
+
372
+ def baseline_shift
373
+ @baseline_shift[-1]
374
+ end
375
+
376
+ def baseline_shift=(value)
377
+ @baseline_shift[-1] = value
378
+ end
379
+
380
+ def text_anchor
381
+ @text_anchor[-1]
382
+ end
383
+
384
+ def text_anchor=(anchor)
385
+ @text_anchor[-1] = anchor
386
+ end
387
+
388
+ def glyph_orientation_vertical
389
+ @glyph_orientation_vertical[-1]
390
+ end
391
+
392
+ def glyph_orientation_vertical=(angle)
393
+ @glyph_orientation_vertical[-1] = angle
394
+ end
395
+
396
+ def glyph_orientation_horizontal
397
+ @glyph_orientation_horizontal[-1]
398
+ end
399
+
400
+ def glyph_orientation_horizontal=(angle)
401
+ @glyph_orientation_horizontal[-1] = angle
402
+ end
403
+
404
+ def letter_spacing
405
+ @letter_spacing[-1]
406
+ end
407
+
408
+ def letter_spacing=(value)
409
+ @letter_spacing[-1] = value
410
+ end
411
+
412
+ def non_default?
413
+ @baseline_shift[-1] != :baseline || @letter_spacing[-1] != 0 ||
414
+ @word_spacing[-1] != 0 || @writing_mode[-1][/\Alr/].nil? ||
415
+ @glyph_orientation_horizontal[-1] != 0
416
+ end
417
+
418
+ def word_spacing
419
+ @word_spacing[-1]
420
+ end
421
+
422
+ def word_spacing=(value)
423
+ @word_spacing[-1] = value
424
+ end
425
+
426
+ def writing_mode
427
+ @writing_mode[-1]
428
+ end
429
+
430
+ def writing_mode=(mode)
431
+ @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
432
+ end
433
+ end # class TextAttributes
434
+
435
+ class GraphicContext
436
+ FONT_STRETCH = {
437
+ normal: Magick::NormalStretch,
438
+ ultra_condensed: Magick::UltraCondensedStretch,
439
+ extra_condensed: Magick::ExtraCondensedStretch,
440
+ condensed: Magick::CondensedStretch,
441
+ semi_condensed: Magick::SemiCondensedStretch,
442
+ semi_expanded: Magick::SemiExpandedStretch,
443
+ expanded: Magick::ExpandedStretch,
444
+ extra_expanded: Magick::ExtraExpandedStretch,
445
+ ultra_expanded: Magick::UltraExpandedStretch
446
+ }
447
+
448
+ FONT_STYLE = {
449
+ normal: Magick::NormalStyle,
450
+ italic: Magick::ItalicStyle,
451
+ oblique: Magick::ObliqueStyle
452
+ }
453
+
454
+ FONT_WEIGHT = {
455
+ 'normal' => Magick::NormalWeight,
456
+ 'bold' => Magick::BoldWeight,
457
+ 'bolder' => Magick::BolderWeight,
458
+ 'lighter' => Magick::LighterWeight
459
+ }
460
+
461
+ TEXT_ANCHOR = {
462
+ start: Magick::StartAnchor,
463
+ middle: Magick::MiddleAnchor,
464
+ end: Magick::EndAnchor
465
+ }
466
+
467
+ ANCHOR_TO_ALIGN = {
468
+ start: Magick::LeftAlign,
469
+ middle: Magick::CenterAlign,
470
+ end: Magick::RightAlign
471
+ }
472
+
473
+ TEXT_DECORATION = {
474
+ none: Magick::NoDecoration,
475
+ underline: Magick::UnderlineDecoration,
476
+ overline: Magick::OverlineDecoration,
477
+ line_through: Magick::LineThroughDecoration
478
+ }
479
+
480
+ TEXT_STRATEGIES = {
481
+ 'lr-tb' => LRTextStrategy,
482
+ 'lr' => LRTextStrategy,
483
+ 'rt-tb' => RLTextStrategy,
484
+ 'rl' => RLTextStrategy,
485
+ 'tb-rl' => TBTextStrategy,
486
+ 'tb' => TBTextStrategy
487
+ }
488
+
489
+ def self.degrees_to_radians(deg)
490
+ Math::PI * (deg % 360.0) / 180.0
491
+ end
492
+
493
+ private
494
+
495
+ def init_matrix
496
+ @rx = @ry = 0
497
+ @sx = @sy = 1
498
+ @tx = @ty = 0
499
+ end
500
+
501
+ def concat_matrix
502
+ curr = @text_attrs.affine
503
+ sx = curr.sx * @sx + curr.ry * @rx
504
+ rx = curr.rx * @sx + curr.sy * @rx
505
+ ry = curr.sx * @ry + curr.ry * @sy
506
+ sy = curr.rx * @ry + curr.sy * @sy
507
+ tx = curr.sx * @tx + curr.ry * @ty + curr.tx
508
+ ty = curr.rx * @tx + curr.sy * @ty + curr.ty
509
+ @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
510
+ init_matrix
511
+ end
512
+
513
+ public
514
+
515
+ attr_reader :gc, :text_attrs
516
+
517
+ def initialize
518
+ @gc = Magick::Draw.new
519
+ @shadow = []
520
+ @shadow << Magick::Draw.new
521
+ @text_attrs = TextAttributes.new
522
+ init_matrix
523
+ end
524
+
525
+ def method_missing(meth_id, *args, &block)
526
+ @gc.__send__(meth_id, *args, &block)
527
+ end
528
+
529
+ def affine(sx, rx, ry, sy, tx, ty)
530
+ sx, rx, ry, sy, tx, ty = Magick::RVG.convert_to_float(sx, rx, ry, sy, tx, ty)
531
+ @gc.affine(sx, rx, ry, sy, tx, ty)
532
+ @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
533
+ nil
534
+ end
535
+
536
+ def baseline_shift(value)
537
+ @text_attrs.baseline_shift = case value
538
+ when 'baseline', 'sub', 'super'
539
+ value.to_sym
540
+ when /[-+]?\d+%/, Numeric
541
+ value
542
+ else
543
+ :baseline
544
+ end
545
+ nil
546
+ end
547
+
548
+ def font(name)
549
+ @gc.font(name)
550
+ @shadow[-1].font = name
551
+ nil
552
+ end
553
+
554
+ def font_family(name)
555
+ @gc.font_family(name)
556
+ @shadow[-1].font_family = name
557
+ nil
558
+ end
559
+
560
+ def font_size(points)
561
+ @gc.font_size(points)
562
+ @shadow[-1].pointsize = points
563
+ nil
564
+ end
565
+
566
+ def font_stretch(stretch)
567
+ stretch = FONT_STRETCH.fetch(stretch.to_sym, Magick::NormalStretch)
568
+ @gc.font_stretch(stretch)
569
+ @shadow[-1].font_stretch = stretch
570
+ nil
571
+ end
572
+
573
+ def font_style(style)
574
+ style = FONT_STYLE.fetch(style.to_sym, Magick::NormalStyle)
575
+ @gc.font_style(style)
576
+ @shadow[-1].font_style = style
577
+ nil
578
+ end
579
+
580
+ def font_weight(weight)
581
+ # If the arg is not in the hash use it directly. Handles numeric values.
582
+ weight = FONT_WEIGHT.fetch(weight) { |key| key }
583
+ @gc.font_weight(weight)
584
+ @shadow[-1].font_weight = weight
585
+ nil
586
+ end
587
+
588
+ def glyph_orientation_horizontal(deg)
589
+ deg = Magick::RVG.convert_one_to_float(deg)
590
+ @text_attrs.glyph_orientation_horizontal = (deg % 360) / 90 * 90
591
+ nil
592
+ end
593
+
594
+ def glyph_orientation_vertical(deg)
595
+ deg = Magick::RVG.convert_one_to_float(deg)
596
+ @text_attrs.glyph_orientation_vertical = (deg % 360) / 90 * 90
597
+ nil
598
+ end
599
+
600
+ def inspect
601
+ @gc.inspect
602
+ end
603
+
604
+ def letter_spacing(value)
605
+ @text_attrs.letter_spacing = Magick::RVG.convert_one_to_float(value)
606
+ nil
607
+ end
608
+
609
+ def push
610
+ @gc.push
611
+ @shadow.push(@shadow.last.dup)
612
+ @text_attrs.push
613
+ nil
614
+ end
615
+
616
+ def pop
617
+ @gc.pop
618
+ @shadow.pop
619
+ @text_attrs.pop
620
+ nil
621
+ end
622
+
623
+ def rotate(degrees)
624
+ degrees = Magick::RVG.convert_one_to_float(degrees)
625
+ @gc.rotate(degrees)
626
+ @sx = Math.cos(GraphicContext.degrees_to_radians(degrees))
627
+ @rx = Math.sin(GraphicContext.degrees_to_radians(degrees))
628
+ @ry = -Math.sin(GraphicContext.degrees_to_radians(degrees))
629
+ @sy = Math.cos(GraphicContext.degrees_to_radians(degrees))
630
+ concat_matrix
631
+ nil
632
+ end
633
+
634
+ def scale(sx, sy)
635
+ sx, sy = Magick::RVG.convert_to_float(sx, sy)
636
+ @gc.scale(sx, sy)
637
+ @sx = sx
638
+ @sy = sy
639
+ concat_matrix
640
+ nil
641
+ end
642
+
643
+ def shadow
644
+ @shadow.last
645
+ end
646
+
647
+ def skewX(degrees)
648
+ degrees = Magick::RVG.convert_one_to_float(degrees)
649
+ @gc.skewX(degrees)
650
+ @ry = Math.tan(GraphicContext.degrees_to_radians(degrees))
651
+ concat_matrix
652
+ nil
653
+ end
654
+
655
+ def skewY(degrees)
656
+ degrees = Magick::RVG.convert_one_to_float(degrees)
657
+ @gc.skewY(degrees)
658
+ @rx = Math.tan(GraphicContext.degrees_to_radians(degrees))
659
+ concat_matrix
660
+ nil
661
+ end
662
+
663
+ def stroke_width(width)
664
+ width = Magick::RVG.convert_one_to_float(width)
665
+ @gc.stroke_width(width)
666
+ @shadow[-1].stroke_width = width
667
+ nil
668
+ end
669
+
670
+ def text(x, y, text)
671
+ return if text.length.zero?
672
+
673
+ text_renderer = if @text_attrs.non_default?
674
+ TEXT_STRATEGIES[@text_attrs.writing_mode].new(self)
675
+ else
676
+ DefaultTextStrategy.new(self)
330
677
  end
331
- [dx, 0]
332
- end
333
678
 
334
- end # class NormalTextStrategy
679
+ text_renderer.render(x, y, text)
680
+ end
335
681
 
336
- end # class Utility
337
- end # class RVG
338
- end # module Magick
682
+ def text_anchor(anchor)
683
+ anchor = anchor.to_sym
684
+ anchor_enum = TEXT_ANCHOR.fetch(anchor, Magick::StartAnchor)
685
+ @gc.text_anchor(anchor_enum)
686
+ align = ANCHOR_TO_ALIGN.fetch(anchor, Magick::LeftAlign)
687
+ @shadow[-1].align = align
688
+ @text_attrs.text_anchor = anchor
689
+ nil
690
+ end
339
691
 
692
+ def text_decoration(decoration)
693
+ decoration = TEXT_DECORATION.fetch(decoration.to_sym, Magick::NoDecoration)
694
+ @gc.decorate(decoration)
695
+ @shadow[-1].decorate = decoration
696
+ nil
697
+ end
340
698
 
699
+ def translate(tx, ty)
700
+ tx, ty = Magick::RVG.convert_to_float(tx, ty)
701
+ @gc.translate(tx, ty)
702
+ @tx = tx
703
+ @ty = ty
704
+ concat_matrix
705
+ nil
706
+ end
341
707
 
708
+ def word_spacing(value)
709
+ @text_attrs.word_spacing = Magick::RVG.convert_one_to_float(value)
710
+ nil
711
+ end
342
712
 
343
- module Magick
344
- class RVG
345
- class Utility
346
-
347
- class TextAttributes
348
-
349
- public
350
-
351
- WRITING_MODE = %w{lr-tb lr rl-tb rl tb-rl tb}
352
-
353
- def initialize()
354
- @affine = Array.new
355
- @affine << Magick::AffineMatrix.new(1, 0, 0, 1, 0, 0)
356
- @baseline_shift = Array.new
357
- @baseline_shift << :baseline
358
- @glyph_orientation_horizontal = Array.new
359
- @glyph_orientation_horizontal << 0
360
- @glyph_orientation_vertical = Array.new
361
- @glyph_orientation_vertical << 90
362
- @letter_spacing = Array.new
363
- @letter_spacing << 0
364
- @text_anchor = Array.new
365
- @text_anchor << :start
366
- @word_spacing = Array.new
367
- @word_spacing << 0
368
- @writing_mode = Array.new
369
- @writing_mode << 'lr-tb'
370
- end
371
-
372
- def push()
373
- @affine.push(@affine.last.dup)
374
- @baseline_shift.push(@baseline_shift.last)
375
- @text_anchor.push(@text_anchor.last)
376
- @writing_mode.push(@writing_mode.last.dup)
377
- @glyph_orientation_vertical.push(@glyph_orientation_vertical.last)
378
- @glyph_orientation_horizontal.push(@glyph_orientation_horizontal.last)
379
- @letter_spacing.push(@letter_spacing.last)
380
- @word_spacing.push(@word_spacing.last)
381
- end
382
-
383
- def pop()
384
- @affine.pop
385
- @baseline_shift.pop
386
- @text_anchor.pop
387
- @writing_mode.pop
388
- @glyph_orientation_vertical.pop
389
- @glyph_orientation_horizontal.pop
390
- @letter_spacing.pop
391
- @word_spacing.pop
392
- end
393
-
394
- def set_affine(sx, rx, ry, sy, tx, ty)
395
- @affine[-1].sx = sx
396
- @affine[-1].rx = rx
397
- @affine[-1].ry = ry
398
- @affine[-1].sy = sy
399
- @affine[-1].tx = tx
400
- @affine[-1].ty = ty
401
- end
402
-
403
- def affine()
404
- @affine[-1]
405
- end
406
-
407
- def baseline_shift()
408
- @baseline_shift[-1]
409
- end
410
-
411
- def baseline_shift=(value)
412
- @baseline_shift[-1] = value
413
- end
414
-
415
- def text_anchor()
416
- @text_anchor[-1]
417
- end
418
-
419
- def text_anchor=(anchor)
420
- @text_anchor[-1] = anchor
421
- end
422
-
423
- def glyph_orientation_vertical()
424
- @glyph_orientation_vertical[-1]
425
- end
426
-
427
- def glyph_orientation_vertical=(angle)
428
- @glyph_orientation_vertical[-1] = angle
429
- end
430
-
431
- def glyph_orientation_horizontal()
432
- @glyph_orientation_horizontal[-1]
433
- end
434
-
435
- def glyph_orientation_horizontal=(angle)
436
- @glyph_orientation_horizontal[-1] = angle
437
- end
438
-
439
- def letter_spacing()
440
- @letter_spacing[-1]
441
- end
442
-
443
- def letter_spacing=(value)
444
- @letter_spacing[-1] = value
445
- end
446
-
447
- def non_default?
448
- @baseline_shift[-1] != :baseline || @letter_spacing[-1] != 0 ||
449
- @word_spacing[-1] != 0 || @writing_mode[-1][/\Alr/].nil? ||
450
- @glyph_orientation_horizontal[-1] != 0
451
- end
452
-
453
- def word_spacing()
454
- @word_spacing[-1]
455
- end
456
-
457
- def word_spacing=(value)
458
- @word_spacing[-1] = value
459
- end
460
-
461
- def writing_mode()
462
- @writing_mode[-1]
463
- end
464
-
465
- def writing_mode=(mode)
466
- @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
467
- end
468
-
469
- end # class TextAttributes
470
-
471
- class GraphicContext
472
-
473
- FONT_STRETCH = {:normal => Magick::NormalStretch,
474
- :ultra_condensed => Magick::UltraCondensedStretch,
475
- :extra_condensed => Magick::ExtraCondensedStretch,
476
- :condensed => Magick::CondensedStretch,
477
- :semi_condensed => Magick::SemiCondensedStretch,
478
- :semi_expanded => Magick::SemiExpandedStretch,
479
- :expanded => Magick::ExpandedStretch,
480
- :extra_expanded => Magick::ExtraExpandedStretch,
481
- :ultra_expanded => Magick::UltraExpandedStretch}
482
-
483
- FONT_STYLE = {:normal => Magick::NormalStyle,
484
- :italic => Magick::ItalicStyle,
485
- :oblique => Magick::ObliqueStyle}
486
-
487
- FONT_WEIGHT = {'normal' => Magick::NormalWeight,
488
- 'bold' => Magick::BoldWeight,
489
- 'bolder' => Magick::BolderWeight,
490
- 'lighter' => Magick::LighterWeight}
491
-
492
- TEXT_ANCHOR = {:start => Magick::StartAnchor,
493
- :middle => Magick::MiddleAnchor,
494
- :end => Magick::EndAnchor}
495
-
496
- ANCHOR_TO_ALIGN = {:start => Magick::LeftAlign,
497
- :middle => Magick::CenterAlign,
498
- :end => Magick::RightAlign}
499
-
500
- TEXT_DECORATION = {:none => Magick::NoDecoration,
501
- :underline => Magick::UnderlineDecoration,
502
- :overline => Magick::OverlineDecoration,
503
- :line_through => Magick::LineThroughDecoration}
504
-
505
- TEXT_STRATEGIES = {'lr-tb'=>LRTextStrategy, 'lr'=>LRTextStrategy,
506
- 'rt-tb'=>RLTextStrategy, 'rl'=>RLTextStrategy,
507
- 'tb-rl'=>TBTextStrategy, 'tb'=>TBTextStrategy}
508
-
509
- def GraphicContext.degrees_to_radians(deg)
510
- Math::PI * (deg % 360.0) / 180.0
511
- end
512
-
513
- private
514
-
515
- def init_matrix()
516
- @rx = @ry = 0
517
- @sx = @sy = 1
518
- @tx = @ty = 0
519
- end
520
-
521
- def concat_matrix()
522
- curr = @text_attrs.affine
523
- sx = curr.sx * @sx + curr.ry * @rx
524
- rx = curr.rx * @sx + curr.sy * @rx
525
- ry = curr.sx * @ry + curr.ry * @sy
526
- sy = curr.rx * @ry + curr.sy * @sy
527
- tx = curr.sx * @tx + curr.ry * @ty + curr.tx
528
- ty = curr.rx * @tx + curr.sy * @ty + curr.ty
529
- @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
530
- init_matrix()
531
- end
532
-
533
- public
534
-
535
- attr_reader :gc, :text_attrs
536
-
537
- def initialize()
538
- @gc = Magick::Draw.new
539
- @shadow = Array.new
540
- @shadow << Magick::Draw.new
541
- @text_attrs = TextAttributes.new
542
- init_matrix()
543
- end
544
-
545
- def method_missing(methID, *args, &block)
546
- @gc.__send__(methID, *args, &block)
547
- end
548
-
549
- def affine(sx, rx, ry, sy, tx, ty)
550
- sx, rx, ry, sy, tx, ty = Magick::RVG.convert_to_float(sx, rx, ry, sy, tx, ty)
551
- @gc.affine(sx, rx, ry, sy, tx, ty)
552
- @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
553
- nil
554
- end
555
-
556
- def baseline_shift(value)
557
- @text_attrs.baseline_shift = case value
558
- when 'baseline', 'sub', 'super'
559
- value.intern
560
- when /[-+]?\d+%/, Numeric
561
- value
562
- else
563
- :baseline
564
- end
565
- nil
566
- end
567
-
568
- def font(name)
569
- @gc.font(name)
570
- @shadow[-1].font = name
571
- nil
572
- end
573
-
574
- def font_family(name)
575
- @gc.font_family(name)
576
- @shadow[-1].font_family = name
577
- nil
578
- end
579
-
580
- def font_size(points)
581
- @gc.font_size(points)
582
- @shadow[-1].pointsize = points
583
- nil
584
- end
585
-
586
- def font_stretch(stretch)
587
- stretch = FONT_STRETCH.fetch(stretch.intern, Magick::NormalStretch)
588
- @gc.font_stretch(stretch)
589
- @shadow[-1].font_stretch = stretch
590
- nil
591
- end
592
-
593
- def font_style(style)
594
- style = FONT_STYLE.fetch(style.intern, Magick::NormalStyle)
595
- @gc.font_style(style)
596
- @shadow[-1].font_style = style
597
- nil
598
- end
599
-
600
- def font_weight(weight)
601
- # If the arg is not in the hash use it directly. Handles numeric values.
602
- weight = FONT_WEIGHT.fetch(weight) {|key| key}
603
- @gc.font_weight(weight)
604
- @shadow[-1].font_weight = weight
605
- nil
606
- end
607
-
608
- def glyph_orientation_horizontal(deg)
609
- deg = Magick::RVG.convert_one_to_float(deg)
610
- @text_attrs.glyph_orientation_horizontal = (deg % 360) / 90 * 90
611
- nil
612
- end
613
-
614
- def glyph_orientation_vertical(deg)
615
- deg = Magick::RVG.convert_one_to_float(deg)
616
- @text_attrs.glyph_orientation_vertical = (deg % 360) / 90 * 90
617
- nil
618
- end
619
-
620
- def inspect()
621
- @gc.inspect
622
- end
623
-
624
- def letter_spacing(value)
625
- @text_attrs.letter_spacing = Magick::RVG.convert_one_to_float(value)
626
- nil
627
- end
628
-
629
- def push()
630
- @gc.push
631
- @shadow.push(@shadow.last.dup)
632
- @text_attrs.push
633
- nil
634
- end
635
-
636
- def pop()
637
- @gc.pop
638
- @shadow.pop
639
- @text_attrs.pop
640
- nil
641
- end
642
-
643
- def rotate(degrees)
644
- degrees = Magick::RVG.convert_one_to_float(degrees)
645
- @gc.rotate(degrees)
646
- @sx = Math.cos(GraphicContext.degrees_to_radians(degrees))
647
- @rx = Math.sin(GraphicContext.degrees_to_radians(degrees))
648
- @ry = -Math.sin(GraphicContext.degrees_to_radians(degrees))
649
- @sy = Math.cos(GraphicContext.degrees_to_radians(degrees))
650
- concat_matrix()
651
- nil
652
- end
653
-
654
- def scale(sx, sy)
655
- sx, sy = Magick::RVG.convert_to_float(sx, sy)
656
- @gc.scale(sx, sy)
657
- @sx, @sy = sx, sy
658
- concat_matrix()
659
- nil
660
- end
661
-
662
- def shadow()
663
- @shadow.last
664
- end
665
-
666
- def skewX(degrees)
667
- degrees = Magick::RVG.convert_one_to_float(degrees)
668
- @gc.skewX(degrees)
669
- @ry = Math.tan(GraphicContext.degrees_to_radians(degrees))
670
- concat_matrix()
671
- nil
672
- end
673
-
674
- def skewY(degrees)
675
- degrees = Magick::RVG.convert_one_to_float(degrees)
676
- @gc.skewY(degrees)
677
- @rx = Math.tan(GraphicContext.degrees_to_radians(degrees))
678
- concat_matrix()
679
- nil
680
- end
681
-
682
- def stroke_width(width)
683
- width = Magick::RVG.convert_one_to_float(width)
684
- @gc.stroke_width(width)
685
- @shadow[-1].stroke_width = width
686
- nil
687
- end
688
-
689
- def text(x, y, text)
690
- return if text.length == 0
691
- if @text_attrs.non_default?
692
- text_renderer = TEXT_STRATEGIES[@text_attrs.writing_mode].new(self)
693
- else
694
- text_renderer = DefaultTextStrategy.new(self)
695
- end
696
-
697
- return text_renderer.render(x, y, text)
698
- end
699
-
700
- def text_anchor(anchor)
701
- anchor = anchor.intern
702
- anchor_enum = TEXT_ANCHOR.fetch(anchor, Magick::StartAnchor)
703
- @gc.text_anchor(anchor_enum)
704
- align = ANCHOR_TO_ALIGN.fetch(anchor, Magick::LeftAlign)
705
- @shadow[-1].align = align
706
- @text_attrs.text_anchor = anchor
707
- nil
708
- end
709
-
710
- def text_decoration(decoration)
711
- decoration = TEXT_DECORATION.fetch(decoration.intern, Magick::NoDecoration)
712
- @gc.decorate(decoration)
713
- @shadow[-1].decorate = decoration
714
- nil
715
- end
716
-
717
- def translate(tx, ty)
718
- tx, ty = Magick::RVG.convert_to_float(tx, ty)
719
- @gc.translate(tx, ty)
720
- @tx, @ty = tx, ty
721
- concat_matrix()
722
- nil
723
- end
724
-
725
- def word_spacing(value)
726
- @text_attrs.word_spacing = Magick::RVG.convert_one_to_float(value)
727
- nil
728
- end
729
-
730
- def writing_mode(mode)
731
- @text_attrs.writing_mode = mode
732
- nil
733
- end
734
-
735
- end # class GraphicContext
736
-
737
- end # class Utility
738
- end # class RVG
713
+ def writing_mode(mode)
714
+ @text_attrs.writing_mode = mode
715
+ nil
716
+ end
717
+ end # class GraphicContext
718
+ end # class Utility
719
+ end # class RVG
739
720
  end # module Magick
740
-