home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / perl_ste.zip / qd.pl < prev   
Perl Script  |  1996-09-12  |  33KB  |  899 lines

  1. #!/usr/local/bin/perl
  2.  
  3. # $Id: qd.pl,v 1.2 1994/09/29 01:24:24 lstein Exp $
  4.  
  5. # This is a package of routines that let you create Macintosh 
  6. # PICT files from within perl.  It implements a subset of Quickdraw
  7. # drawing commands, primarily those related to line drawing, rectangles,
  8. # ovals, polygons, and text.  Flagrantly absent are: regions and the 
  9. # snazzy color transfer modes.  Regions are absent because they were
  10. # more trouble than I had time for, and the transfer modes because I
  11. # never use them.  (The latter shouldn't be too hard to add.)  Also 
  12. # missing are the pixmap commands.  If you want to do pixmaps, you
  13. # should be using the ppm utilities.
  14.  
  15. # A QUICK TUTORIAL ON QUICKDRAW
  16. # Quickdraw is not Postscript.  You cannot write routines in it or get
  17. # (any useful) information out of it.  Quickdraw pictures are a series of
  18. # drawing commands, concatenated together in a binary format.
  19. #
  20. # A Macintosh picture consists of a header describing the size of the
  21. # picture and its bounding rectangle, followed by a series of drawing
  22. # commands, followed by a termination code.  This perl library is
  23. # modeled closely on the way that you would draw a picture on the Mac.
  24. # First you open the picture with the &qd'OpenPicture() command.  This
  25. # initializes some data structures.  Then you call a series of drawing
  26. # subroutines, such as &qd'TextFont(), &qd'MoveTo(), &qd'DrawString().
  27. # These routines append their data to the growing (but still private)
  28. # picture.  You then close the picture with &qd'ClosePicture.  This
  29. # returns a scalar variable containing the binary picture data.
  30.  
  31. # RECTANGLES
  32. #
  33. # To open a picture you need to define a rectangle that will serve as
  34. # its frame and will define its drawing area.  The rectangle is (of 
  35. # course) a binary structure.  The following utilities allow you to
  36. # create and manipulate rectangles:
  37. #
  38. #   &qd'SetRect(*myRect,left,top,right,bottom); # Set the sides of $myRect
  39. #   &qd'OffsetRect(*myRect,deltaH,deltaV);      # Shift the rectangle as indicated
  40. #   &qd'InsetRect(*myRect,deltaH,deltaV);       # Shrink rectangle by size indicated
  41.  
  42.  
  43. # OPENING A PICTURE
  44. #
  45. # Pass a previously-defined rectangle to the routine OpenPicture.  Only one picture
  46. # may be open at a time.  The rectangle defines the drawing area in pixels.
  47. # A printer page is 8.5 x 11 inches, at 72 pixels per inch = 612 x 792 pixels.
  48. #
  49. #   &qd'OpenPicture($myRect);
  50. #
  51. # You will next very likely want to set the clipping rectangle to the same rectangle
  52. # you used to open the picture with.  Clipping rectangles limit quickdraw's drawing
  53. # to the area within the rectangle.  Even if you don't use clipping, however, it's a
  54. # good idea to define the rectangle because some drawing programs behave eratically
  55. # when displaying unclipped pictures.
  56. # You then issue drawing commands.  When you're done you can get the picture data with
  57. # something like $pictData = &qd'ClosePicture;
  58.  
  59. # SETTING THE FOREGROUND AND BACKGROUND COLORS
  60. #
  61. # The foreground color is the color of the ink when a "frame" or "paint" command
  62. # is given.  The background color is the color of the erased area when an "erase"
  63. # command is given.  The defaults are black and white.  The colors can be changed
  64. # in either of two ways:
  65. #
  66. #  1. The "old" 8-color system: black, white, red, green, blue, cyan, magenta, yellow
  67. #     Call the routines &qd'FgColor() and &qd'BgColor() with one of the constants
  68. #     $qd'REDCOLOR,$qd'GREENCOLOR, etc.  This gives you a limited number of highly
  69. #     satured colors.
  70. #
  71. #  2. The new 24-bit color system.  Call the routines &qd'RGBForeColor() and 
  72. #     &qd'RGBBackColor(), passing the routines the red, green and blue components
  73. #     of the color.  These components are two-byte unsigned integers, so you can choose
  74. #     any value between 0x000 and 0xFFFF.  Higher is darker, so:
  75. #     (0x0000,0x0000,0x0000) = BLACK
  76. #     (0xFFFFF,0xFFFF,0xFFFF) = WHITE
  77. #     (0xFFFFF,0x0000,0x0000) = PURE RED
  78. #     etc.
  79.  
  80.  
  81. # SETTING THE PATTERN
  82. #
  83. # Like colors, the drawing commands use the current pattern, a 32 row x 32 column 
  84. # bit array that defines the pattern of the "ink".
  85. # The default pattern is $qd'BLACK, which is solid black.  The only
  86. # other pattern I've defined is $qd'GRAY, which is a 50% checkerboard.  You
  87. # might want to define others.
  88. #
  89. # The current pattern is set using &qd'PenPat($myPattern).
  90.  
  91.  
  92. # LINE DRAWING
  93. #
  94. # Quickdraw has the concept of the "current point" of the pen.  Generally
  95. # you move the pen to a point and then start drawing.  The next time you draw,
  96. # the pen will be wherever the last drawing command left it.  In addition, the
  97. # pen has a width, a pattern and a color.  In the below descriptions, 
  98. # h=horizontal, v=vertical
  99. #
  100. # &qd'MoveTo(h,v)           # Move to indicated coordinates (0,0 is upper left of picture)
  101. # &qd'LineTo(h,v)           # Draw from current position to indicated position
  102. # &qd'Line(dh,dv)           # Draw a line dh pixels horizontally, dv pixels vertically,
  103. #                             starting at current position
  104. # &qd'PenSize(h,v)          # Set the size of the pen to h pixels wide, v pixels high
  105.  
  106.  
  107. # PEN SCALING
  108. #
  109. # The original quickdraw was incapable of drawing at higher than the screen resolution,
  110. # so even if the PenSize is set to (1,1) the lines will appear chunky when printed out
  111. # on the laserwriter (which has four times the resolution of the screen).  Call 
  112. # &qd'Scale(1,4) to fix this problem by shrinking the pen down to a quarter of its
  113. # (1,1) size.
  114. #
  115. # &qd'Scale(numerator,denominator) # Scale the pen by the fraction numerator/denominator
  116.  
  117.  
  118. # TEXT
  119. #
  120. # &qd'TextFont(fontCode)    # Set the current font to indicated code.  Currently
  121. #                             defined fonts are $qd'TIMES, $qd'NEWCENTURYSCHOOLBK,
  122. #                             $qd'SYMBOL, $qd'HELVETICA, and $qd'COURIER.
  123. #
  124. # &qd'TextSize(size)        # Set the current font size (in points).  12 point is typical
  125. #
  126. # &qd'TextFace(attributes)  # Set one or more font style attributes.  Currently defined
  127. #                             are $qd'PLAIN, $qd'BOLD, $qd'ITALIC, $qd'UNDERLINE, and
  128. #                             can be used in combination: 
  129. #                             &qd'TextFace($qd'BOLD + $qd'ITALIC);
  130. #
  131. # &qd'DrawString(string)    # Draw the indicated text.  It will be drawn from the
  132. #                             current pen location.  Word wrap is NOT supported.
  133. #                             Rotated text is NOT supported.
  134. #
  135. # &qd'TextWidth(string)     # This will return an approximate width for the string
  136. #                             when it is printed in the current size, font and face.
  137. #                             Unfortunately, since perl has no access to the Macintosh
  138. #                             font description tables, the number returned by this
  139. #                             routine will be wildly inaccurate at best.
  140. #                             However, if you have X11R5 bdf fonts installed, we look 
  141. #                             in the directory $qd'X11FONTS in order to find a bdf metrics
  142. #                             font to use.  This will give you extremely accurate measurements.
  143. #                             Please set this variable to whatever is correct for your local
  144. #                             system.  To add more fonts, put them in your bdf font directory
  145. #                             and update the %qd'font_metric_files array at the bottom of this
  146. #                             file.  It maps a key consisting of the Quickdraw font number, 
  147. #                             font size, and font style (0 for plain, 1 for italic, 2 for bold,
  148. #                             3 for both) to the appropriate bdf file.
  149.  
  150. # RECTANGLES
  151. #
  152. # Draw rectangles using the routines:
  153. #   &qd'FrameRect($myRect);                     # Draw wire-frame rectangle
  154. #   &qd'PaintRect($myRect);                     # Fill rectangle with current foreground
  155. #                                                 color and pattern
  156. #   &qd'EraseRect($myRect);                     # Erase the rectangle (fill with bg color)
  157. #   &qd'InvertRect($myRect);                    # Invert black and white in rectangle
  158.  
  159.  
  160. # OVALS
  161. #
  162. # Draw ovals using the routines:
  163. #   &qd'FrameOval($myRect);                     # Draw wire-frame oval
  164. #   &qd'PaintOval($myRect);                     # Fill oval with current foreground
  165. #                                                 color and pattern
  166. #   &qd'EraseOval($myRect);                     # Erase the oval (fill with bg color)
  167. #   &qd'InvertOval($myRect);                    # Invert black and white in oval
  168. #   &qd'FillOval($myRect,$pat);                 # Fill with specified pattern
  169.  
  170. # ROUND RECTANGLES
  171. # Draw round-cornered rectangles with these routines.  They each take an oval radius
  172. # to determine the amount of curvature.  Values of 10-20 are typical.
  173. #   &qd'FrameRoundRect($myRect,$ovalWidth,$ovalHeight); # wire-frame outline
  174. #   &qd'PaintRoundRect($myRect,$ovalWidth,$ovalHeight); # fill with current foreground
  175. #   &qd'EraseRoundRect($myRect,$ovalWidth,$ovalHeight); # erase
  176. #   &qd'InvertRoundRect($myRect,$ovalWidth,$ovalHeight);# invert
  177. #   &qd'FillRoundRect($myRect,$ovalWidth,$ovalHeight,$pat); # fill with specified pattern
  178.  
  179. # ARCS
  180. # Draw an arc subtending the specified rectangle.  Angles are in degrees and
  181. # start pointing northward and get larger clockwise:
  182. # e.g. PaintArc($r,45,90) gives you a pie wedge from 2 o'clock to 5 o'clock
  183. #   &qd'FrameArc($rect,$startAngle,$arcAngle);  # wire-frame the arc
  184. #   &qd'PaintArc($rect,$startAngle,$arcAngle);  # fill with current foreground
  185. #   &qd'EraseArc($rect,$startAngle,$arcAngle);  # erase arc
  186. #   &qd'InvertArc($rect,$startAngle,$arcAngle);  # flip white and black
  187. #   &qd'FillArc($rect,,$startAngle,$arcAngle,$pat);  # fill with specified pattern
  188.  
  189. # POLYGONS
  190. # Calling OpenPoly returns the name of a variable in which a growing
  191. # polygon structure will be stored.  Once a polygon is opened, all drawing
  192. # commands cease to have an effect on the picture.  Instead, all MoveTo,
  193. # LineTo and Line commands accumulate polygon vertices into the data structure.
  194. # Call ClosePoly to stop recording drawing commands.  The polygon can now
  195. # be moved, scaled, drawn, filled and erased as many times as wished.  Call
  196. # KillPoly to release the memory taken up by the polygon
  197. #   $polygon = &qd'OpenPoly;                      # begin recording drawing commands
  198. #   &qd'ClosePoly($polygon);                      # stop recording drawing commands
  199. #   &qd'FramePoly($polygon);                      # wire-frame the polygon
  200. #   &qd'PaintPoly($polygon);                      # fill with current foreground
  201. #   &qd'ErasePoly($polygon);                      # erase polygon
  202. #   &qd'FillPoly($polygon,$pat);                  # fill polygon with pattern
  203. #   &qd'OffsetPoly($polygon,$dh,$dv);             # translate poly by dh horizontally, dv vertically
  204. #   &qd'MapPoly($polygon,$srcRect,$destRect);     # map polygon from coordinate system defined by
  205.                                                   #  source rectangle to that defined by destination
  206.                                                   #  rectangle (moving or resizing it as needed)
  207.  
  208. # PRINTING OUT THE PICTURE IN A FORM THAT THE MACINTOSH CAN READ
  209. #
  210. # The Mac expects its picture files to begin with 512 bytes of "application specific"
  211. # data.  By default the picture data that you get will be proceeded by 512 bytes of
  212. # 0's.  If you want something else, or if you just want the picture data, set the
  213. # package variable $qd'PICTHEADER to whatever you desire before calling ClosePicture.
  214. # In order for the picture data to be readable on the Macintosh, the file type must
  215. # be set to 'PICT'.  A number of UNIX utilities, including mcvert and BinHex allow
  216. # you to do this.  Or you can use the picttoppm utility (part of the netppm suite of
  217. # graphics tools) to translate the file into any format you desire.
  218.  
  219. # A WORKING EXAMPLE
  220. # require "qd.pl";
  221. # &qd'SetRect(*myRect,0,0,500,500);          # Define a 500 pixel square
  222. # &qd'OpenPicture($myRect);                  # Begin defining the picture
  223. # &qd'ClipRect($myRect);                     # Always a good idea
  224. # &qd'MoveTo(5,5);                           # Move the pen to a starting point
  225. # &qd'LineTo(400,400);                       # A diagonal line
  226. # &qd'TextFont($qd'COURIER);                 # Set the font
  227. # &qd'MoveTo(50,20);                         # Move the pen to a new starting point
  228. # &qd'DrawString("Hello there!");            # Friendly greeting
  229. # &qd'SetRect(*myRect,80,80,250,250);        # New rectangle
  230. # &qd'RGBForeColor(0x0000,0x0000,0xFFFF);    # Set the color to blue
  231. # &qd'PaintRect($myRect);                    # Fill rectangle with that color
  232. # $data = &qd'ClosePicture;                  # Close picture and retrieve data
  233.  
  234. #  # Pipe through binhex, setting the creator type to JVWR for JPEG Viewer
  235. #  # Note: BinHex is available at <ftp://genome.wi.mit.edu/software/util/BinHex>
  236. # open (BINHEX "| BinHex -t PICT -c JVWR -n 'An Example'"); 
  237. # print BINHEX $data;
  238. # close BINHEX;
  239.  
  240. # # Turn it into a GIF file, using the ppm utilities
  241. # open (GIF, "| picttoppm | ppmtogif -transparent white");
  242. # print GIF $data;
  243. # close GIF;
  244.  
  245.  
  246. # MISCELLANEOUS NOTES
  247. # NOTE: For some reason the various FILL routines don't work as
  248. # advertised.  They are simulated by a PnPat followed by a paint
  249.  
  250. # --------------------------------------------------------------------
  251. # Quickdraw-like functions -- now using PICT2
  252. # --------------------------------------------------------------------
  253. {
  254. package qd;
  255.  
  256. # Directory to look in to find font metric definitions -- change this
  257. # for your installation
  258. $X11FONTS = '/usr/local/X11R5/X11/fonts/bdf';
  259.  
  260. # Apple quickdraw constants
  261. $TIMES = 20;
  262. $HELVETICA = 21;
  263. $COURIER = 22;
  264. $SYMBOL = 23;
  265. $NEWCENTURYSCHOOLBK = 34;
  266.  
  267. $PLAIN = 0;
  268. $BOLD = 1;
  269. $ITALIC = 2;
  270. $UNDERLINE = 4;
  271.  
  272. # Some minimal patterns -- define your own if you like
  273. $GRAY = pack ('n4',0xAA55,0xAA55,0xAA55,0xAA55);
  274. $DKGRAY = pack ('n4',0xDD77,0xDD77,0xDD77,0xDD77);
  275. $LTGRAY = pack ('n4',0x8822,0x8822,0x8822,0x8822);
  276. $WHITE = pack('n4',0x0000,0x0000,0x0000,0x0000);
  277. $BLACK = pack ('n4',0xFFFF,0xFFFF,0xFFFF,0xFFFF);
  278.  
  279. # absolute colors to be used with FgColor/BgColor
  280. # (for better control, use RGBFgColor/RGBBgColor)
  281. $BLACKCOLOR = 33;
  282. $WHITECOLOR = 30;
  283. $REDCOLOR = 209;
  284. $GREENCOLOR = 329;
  285. $BLUECOLOR = 389;
  286. $CYANCOLOR = 269;
  287. $MAGENTACOLOR = 149;
  288. $YELLOWCOLOR = 89;
  289.  
  290. # This defines the header used at the beginning of PICT files:
  291. $PICTHEADER = "\0" x 512;
  292.  
  293. # These are phoney font metrics which we use when no font metrics files are
  294. # around to help us out.
  295. $fudgefactor = 0.55;
  296. $ITALICEXTRA = 0.05;
  297. $BOLDEXTRA = 0.08;
  298.  
  299. # Initial starting values
  300. $textFont = $HELVETICA;
  301. $textSize = 12;
  302. $textFace = $PLAIN;
  303. $rgbfgcolor = pack('n*',0xFFFF,0xFFFF,0xFFFF);
  304. $rgbbgcolor = pack('n*',0,0,0);
  305. $fgcolor = $BLACKCOLOR;
  306. $bgcolor = $WHITECOLOR;
  307. $polySave = undef;
  308.  
  309. $_PnPattern = $BLACK;
  310. $_polyName = "polygon000";
  311.  
  312. sub OpenPicture {               # begin a picture
  313.     local($rect) = @_;
  314.     $currH = $currV = 0;        # current pen position
  315.     $pict = $PICTHEADER;        # the header
  316.     $pict .= pack('n',0);       # size int (placeholder)
  317.     $pict .= $rect;             # pict frame
  318.     $pict .= pack('n',0x0011);  # Type 2 picture
  319.     $pict .= pack('n',0x02FF);  # version number
  320.     $pict .= pack('nC24',0x0C00,0);     # reserved header opcode + 24 bytes of reserved data
  321.     # initialize the font and size
  322.     &TextFont($textFont);
  323.     &TextSize($textSize);
  324.     &TextFace($textFace);
  325. }
  326.  
  327. sub ClosePicture {              # close pict and return it
  328.     $pict .= pack ('n',0x00FF); # end of pict code
  329.     substr($pict,512,2) = pack('n',length($pict) - 512); # fill in length 
  330.     return $pict;
  331. }
  332.  
  333. sub ClipRect {
  334.     local($rect) = @_;
  335.     $pict .= pack('nn',0x0001,0x0A) . $rect;
  336. }
  337.  
  338. sub PenPat {
  339.     local($newpat) = @_;
  340.     return unless $newpat ne $_PnPattern;
  341.     $_PnPattern = $newpat;
  342.     $pict .= pack('n',0x0009) . $_PnPattern;
  343. }
  344.  
  345. sub RGBForeColor {
  346.     local($rgb) = pack('n3',@_);
  347.     return unless $rgb ne $rgbfgcolor;
  348.     $rgbfgcolor = $rgb;
  349.     $pict .= pack('n',0x001A) . $rgbfgcolor;
  350. }
  351.  
  352. sub RGBBackColor {
  353.     local($rgb) = pack('n3',@_);
  354.     return unless $rgb ne $rgbbgcolor;
  355.     $rgbbgcolor = $rgb;
  356.     $pict .= pack('n',0x001B) . $rgbbgcolor;
  357. }
  358.  
  359. sub FgColor {
  360.     local($color) = @_;
  361.     return unless $color != $fgcolor;
  362.     $fgcolor = $color;
  363.     $pict .= pack('nL',0x000E,$color);
  364. }
  365.  
  366. sub BgColor {
  367.     local($color) = @_;
  368.     return unless $color != $bgcolor;
  369.     $bgcolor = $color;
  370.     $pict .= pack('nL',0x000F,$color);
  371. }
  372.  
  373. sub TextFont {
  374.     local($font) = @_;
  375.     $textFont = $font;
  376.     $pict .= pack('nn',0x0003,$font);
  377. }
  378.  
  379. sub TextSize {
  380.     local($size) = @_;
  381.     $textSize = $size;
  382.     $pict .= pack('nn',0x000D,$size);
  383. }
  384.  
  385. sub PenSize {
  386.     local($h,$v) = @_;
  387.     $pict .= pack('nnn',0x0007,$v,$h);
  388. }
  389.  
  390. sub TextFace {
  391.     return if $textFace == @_[0];
  392.     $textFace = @_[0];
  393.     $pict .= pack ('nCC',0x0004,$textFace,0); # (zero added to pad to word)
  394. }
  395.  
  396. sub DrawString {
  397.     local($text) = @_;
  398.     $text .= "\0" x ((length($text) + 1) % 2); # pad text to an odd length
  399.     $pict .= pack('nnnC',0x0028,$currV,$currH,length($text)) . $text;
  400. }
  401.  
  402. # RECTANGLE MANIPULATION ROUTINES.  Note that
  403. # the rectangles are passed by NAME rather than by value,
  404. # in accordance with the MacOS way of doing things.
  405. sub SetRect {
  406.     local(*r,$h1,$v1,$h2,$v2) = @_;
  407.     $r = pack ('n4',$v1,$h1,$v2,$h2);
  408. }
  409.  
  410. sub OffsetRect {
  411.     local(*r,$x,$y) = @_;
  412.     local($v1,$h1,$v2,$h2) = unpack('n4',$r);
  413.     $h1 += $x; $h2 += $x;
  414.     $v1 += $y; $v2 += $y;
  415.     $r = pack ('n4',$v1,$h1,$v2,$h2);    
  416. }
  417.  
  418. sub InsetRect {
  419.     local(*r,$x,$y) = @_;
  420.     local($v1,$h1,$v2,$h2) = unpack('n4',$r);
  421.     $h1 -= int($x/2); $h2 -= int($x/2);
  422.     $v1 -= int($y/2); $v2 -= int($y/2);
  423.     $r = pack ('n4',$v1,$h1,$v2,$h2);    
  424. }
  425.  
  426. # A few utility routine to translate between perl
  427. # arrays and rectangles.
  428.  
  429. # four-element perl array to quickdraw rect structure
  430. sub a2r {
  431.     local($top,$left,$bottom,$right) = @_;
  432.     return pack('n4',$top,$left,$bottom,$right);
  433. }
  434.  
  435. # rectangle to four-element perl array
  436. sub r2a {
  437.     local($rect) = @_;
  438.     return unpack('n4',$rect);
  439. }
  440.  
  441. # associative array in which the keys are 'top','left','bottom','right'
  442. # to quickdraw rect structure
  443. sub aa2r {
  444.     local(%r) = @_;
  445.     return pack('n4',$r{'top'},$r{'left'},$r{'bottom'},$r{'right'});
  446. }
  447.  
  448. # quickdraw rect structure to associative array
  449. sub r2aa {
  450.     local($r) = @_;
  451.     local(%r);
  452.     ($r{'top'},$r{'left'},$r{'bottom'},$r{'right'}) = unpack('n4',$r);
  453.     return %r;
  454. }
  455.  
  456. # LINE DRAWING ROUTINES
  457. sub MoveTo {
  458.     ($currH,$currV) = @_;
  459. }
  460.  
  461. sub Move {
  462.     local($dh,$dv) = @_;
  463.     $currH += $dh;
  464.     $currV += $dv;
  465. }
  466.  
  467. sub LineTo {
  468.     local($h,$v) = @_;
  469.     # Special handling for polygons
  470.     if (defined(@polySave)) {
  471.         &_addVertex(*polySave,$h,$v)
  472.     } else {
  473.         $pict .= pack('nn4',0x0020,$currV,$currH,$v,$h);
  474.     }
  475.     ($currH,$currV) = ($h,$v);
  476. }
  477.  
  478. sub Line {
  479.     local($dh,$dv) = @_;
  480.     # Special handling for polygons
  481.     if (defined(@polySave)) {
  482.         &_addVertex(*polySave,$h,$v);
  483.     } else {
  484.         $pict .= pack('nn4',0x0020,$currV,$currH,$currV+$dv,$currH+$dh);
  485.     }
  486.     ($currH,$currV) = ($currH+$dh,$currV+$dv);
  487. }
  488.  
  489. sub Scale { #use picComment to set laserwriter line scaling
  490.     local($numerator,$denominator)= @_;
  491.     $pict .= pack('nnnn2',0x00A1,182,4,$numerator,$denominator);
  492. }
  493.  
  494.  
  495. # Rectangles
  496. sub FrameRect {
  497.     local($rect) = @_;
  498.     $pict .= pack('n',0x0030) . $rect;
  499. }
  500.  
  501. sub PaintRect {
  502.     local($rect) = @_;
  503.     $pict .= pack('n',0x0031) . $rect;
  504. }
  505.  
  506. sub EraseRect {
  507.     local($rect) = @_;
  508.     $pict .= pack('n',0x0032) . $rect;
  509. }
  510.  
  511. sub InvertRect {
  512.     local($rect) = @_;
  513.     $pict .= pack('n',0x0033) . $rect;
  514. }
  515.  
  516. sub FillRect {
  517.     local($rect,$pattern) = @_;
  518.     local($oldpat) = $_PnPattern;
  519.     &PenPat($pattern);
  520.     &PaintRect($rect);
  521.     &PenPat($oldpat);
  522. }
  523.  
  524. # Ovals
  525. sub FrameOval {
  526.     local($rect) = @_;
  527.     $pict .= pack('n',0x0050) . $rect;
  528. }
  529.  
  530. sub PaintOval {
  531.     local($rect) = @_;
  532.     $pict .= pack('n',0x0051) . $rect;
  533. }
  534.  
  535. sub EraseOval {
  536.     local($rect) = @_;
  537.     $pict .= pack('n',0x0052) . $rect;
  538. }
  539.  
  540. sub InvertOval {
  541.     local($rect) = @_;
  542.     $pict .= pack('n',0x0053) . $rect;
  543. }
  544.  
  545. sub FillOval {
  546.     local($rect,$pattern) = @_;
  547.     local($oldpat) = $_PnPattern;
  548.     &PenPat($pattern);
  549.     &PaintOval($rect);
  550.     &PenPat($oldpat);
  551. }
  552.  
  553. # Arcs
  554. sub FrameArc {
  555.     local($rect,$startAngle,$arcAngle) = @_;
  556.     $pict .= pack('n',0x0060) . $rect;
  557.     $pict .= pack('nn',$startAngle,$arcAngle);
  558. }
  559.  
  560. sub PaintArc {
  561.     local($rect,$startAngle,$arcAngle) = @_;
  562.     $pict .= pack('n',0x0061) . $rect;
  563.     $pict .= pack('nn',$startAngle,$arcAngle);
  564. }
  565.  
  566. sub EraseArc {
  567.     local($rect,$startAngle,$arcAngle) = @_;
  568.     $pict .= pack('n',0x0062) . $rect;
  569.     $pict .= pack('nn',$startAngle,$arcAngle);
  570. }
  571.  
  572. sub InvertArc {
  573.     local($rect,$startAngle,$arcAngle) = @_;
  574.     $pict .= pack('n',0x0063) . $rect;
  575.     $pict .= pack('nn',$startAngle,$arcAngle);
  576. }
  577.  
  578. sub FillArc {
  579.     local($rect,$startAngle,$arcAngle,$pattern) = @_;
  580.     local($oldpat) = $_PnPattern;
  581.     &PenPat($pattern);
  582.     &PaintArc($rect,$startAngle,$arcAngle);
  583.     &PenPat($oldpat);
  584. }
  585.  
  586. # Round rects
  587. sub FrameRoundRect {
  588.     local($rect,$ovalWidth,$ovalHeight) = @_;
  589.     unless ($_roundRectCurvature eq "$ovalWidth $ovalHeight") {
  590.         $pict .= pack('nn2',0x000B,$ovalHeight,$ovalWidth);
  591.         $_roundRectCurvature = "$ovalWidth $ovalHeight";
  592.     }
  593.     $pict .= pack('n',0x0040) . $rect;
  594. }
  595.  
  596. sub PaintRoundRect {
  597.     local($rect,$ovalWidth,$ovalHeight) = @_;
  598.     unless ($_roundRectCurvature eq "$ovalWidth $ovalHeight") {
  599.         $pict .= pack('nn2',0x000B,$ovalHeight,$ovalWidth);
  600.         $_roundRectCurvature = "$ovalWidth $ovalHeight";
  601.     }
  602.     $pict .= pack('n',0x0041) . $rect;
  603. }
  604.  
  605. sub EraseRoundRect {
  606.     local($rect,$ovalWidth,$ovalHeight) = @_;
  607.     unless ($_roundRectCurvature eq "$ovalWidth $ovalHeight") {
  608.         $pict .= pack('nn2',0x000B,$ovalHeight,$ovalWidth);
  609.         $_roundRectCurvature = "$ovalWidth $ovalHeight";
  610.     }
  611.     $pict .= pack('n',0x0042) . $rect;
  612. }
  613.  
  614. sub InvertRoundRect {
  615.     local($rect,$ovalWidth,$ovalHeight) = @_;
  616.     unless ($_roundRectCurvature eq "$ovalWidth $ovalHeight") {
  617.         $pict .= pack('nn2',0x000B,$ovalHeight,$ovalWidth);
  618.         $_roundRectCurvature = "$ovalWidth $ovalHeight";
  619.     }
  620.     $pict .= pack('n',0x0043) . $rect;
  621. }
  622.  
  623. sub FillRoundRect {
  624.     local($rect,$ovalWidth,$ovalHeight,$pattern) = @_;
  625.     local($oldpat) = $_PnPattern;
  626.     &PenPat($pattern);
  627.     &PaintRoundRect($rect,$ovalWidth,$ovalHeight);
  628.     &PenPat($oldpat);
  629. }
  630.  
  631. # Polygons -- you are only allowed to create one polygon at a time.
  632. # You will be returned a "handle" which contains the growing polygon
  633. # structure.  The "handle" is actually the NAME of the scalar
  634. sub OpenPoly {
  635.     $_polyName++;
  636.     undef $polySave;            # close one if it was already defined
  637.     *polySave = $_polyName;
  638.     @polySave = (10,0,0,0,0); # initialize it to empty size and rectangle
  639.     return $_polyName;
  640. }
  641.  
  642. sub ClosePoly {
  643.     *polySave = 'scratch';
  644.     undef @polySave;
  645. }
  646.  
  647. # Kill the poly -- really a no-op in perl
  648. sub KillPoly {
  649.     local(*poly) = @_;
  650.     undef @poly;
  651. }
  652.  
  653. # Polygon drawing
  654. sub FramePoly {
  655.     local(*poly) = @_;
  656.     return unless @poly;
  657.     $pict .= pack('n*',0x0070,@poly);
  658. }
  659.  
  660. sub PaintPoly {
  661.     local(*poly) = @_;
  662.     return unless @poly;
  663.     $pict .= pack('n*',0x0071,@poly);
  664. }
  665.  
  666. sub ErasePoly {
  667.     local(*poly) = @_;
  668.     return unless @poly;
  669.     $pict .= pack('n*',0x0072,@poly);
  670. }
  671.  
  672. sub InvertPoly {
  673.     local(*poly) = @_;
  674.     return unless @poly;
  675.     $pict .= pack('n*',0x0073,@poly);
  676. }
  677.  
  678. sub FillPoly {
  679.     local(*poly,$pattern) = @_;
  680.     return unless @poly;
  681.     local($oldpat) = $_PnPattern;
  682.     &PenPat($pattern);
  683.     &PaintPoly(*poly);
  684.     &PenPat($oldpat);
  685. }
  686.  
  687. sub OffsetPoly {
  688.     local(*poly,$dh,$dv) = @_; 
  689.   return unless @poly;
  690.     local($size,@vertices) = @poly;
  691.     local($i);
  692.     for ($i=0;$i<@vertices;$i+=2) {
  693.         $vertices[$i] += $dv;
  694.         $vertices[$i+1] += $dh;
  695.     }
  696.     @poly = ($size,@vertices);
  697. }
  698.  
  699. sub MapPoly {
  700.     local(*poly,$srcRect,$destRect) = @_;
  701.     return unless @poly;
  702.     local($size,@vertices) = @poly;
  703.     local(@src) = unpack('n4',$srcRect);
  704.     local(@dest) = unpack('n4',$destRect);
  705.     local($factorV) = ($dest[2]-$dest[0])/($src[2]-$src[0]);
  706.     local($factorH) = ($dest[3]-$dest[1])/($src[3]-$src[1]);
  707.     for ($i=0;$i<@vertices;$i+=2) {
  708.         $vertices[$i] = int($dest[0] + ($vertices[$i] - $src[0]) * $factorV);
  709.         $vertices[$i+1] = int($dest[1] + ($vertices[$i+1] - $src[1]) * $factorH);
  710.     }
  711.     @poly = ($size,@vertices);
  712. }
  713.  
  714. # A utility routine to add a vertex to the growing polygon structure
  715. # We need to grow both the size of the polygon and increase the bounding
  716. # rectangle.  A special case occurs when we add the first vertex:
  717. # we store both the current position 
  718. sub _addVertex {
  719.     local(*polygon,$h,$v) = @_;
  720.     local($size,$top,$left,$bottom,$right,@vertices) = @polygon;
  721.     # Special case for empty vertices -- add the current point
  722.     unless (@vertices) {
  723.         push(@vertices,$currV,$currH);
  724.         $size += 4;
  725.         $top = $bottom = $currV;
  726.         $left = $right = $currH;
  727.     }
  728.  
  729.     # IM V1 implies that all vertices are stored relative to
  730.     # the first point -- I don't know if this is really the case
  731.     push (@vertices,$v,$h);
  732.  
  733.     $size += 4;
  734.     $top = $v if $v < $top;
  735.     $bottom = $v if $v > $bottom;
  736.     $left = $h if $h < $left;
  737.     $right = $h if $h > $right;
  738.     @polygon=($size,$top,$left,$bottom,$right,@vertices);
  739. }
  740.  
  741. # We try to get the metrics from an X11 bdf font file, if possible.
  742. sub TextWidth {
  743.     local($text) = @_;
  744.  
  745.     # See if we can derive the character widths from a metrics file
  746.     local($face) = 0xFB & $textFace; # underlining don't count
  747.     local($metric_name) = &_getFontMetrics($textFont,$textSize,$face);
  748.     if ($metric_name && (*metrics = $metric_name) && defined(%metrics)) {
  749.         local($length);
  750.         foreach (split('',$text)) {
  751.             $length += $metrics{ord($_)};
  752.         }
  753.         return $length;
  754.     } else {                    # we get here if we don't have any metrics - make it up
  755.         local($extra);
  756.         $extra += $ITALICEXTRA if vec($textFace,$ITALIC,1);
  757.         $extra += $BOLDEXTRA if vec($textFace,$BOLD,1);
  758.         return length($text) * $textSize * ($fudgefactor+$extra);
  759.     }
  760. }
  761.  
  762. # Utility routine to read text widths out of bdf files.  We create a metrics
  763. # array on the fly.  The names of the metrics files are stored in an array
  764. # called _metricsArrays.  We return the name of the array, or undef if inapplicable.
  765. sub _getFontMetrics {
  766.     local($font,$size,$face) = @_;
  767.     local($key) = "$font $size $face";
  768.     return $_metricsArrays{$key} if $_metricsArrays{$key};
  769.  
  770.     # If we get here, we don't have a metrics array to return.  See if we can
  771.     # construct one from a bdf file.
  772.  
  773.     # Don't bother unless this font is defined.
  774.     return undef unless $font_metric_files{$key};
  775.  
  776.     # Don't bother if we tried before and failed
  777.     return undef if $_failed_metric{$key};
  778.  
  779.     # Try to open up the bdf file.  Remember if we fail
  780.     unless (open(BDF,"$font_metric_files{$key}")) {
  781.         $_failed_metric_files{$key}++;
  782.         return undef;
  783.     }
  784.  
  785.     # Wow! We're golden.  Create a new metrics array
  786.     $next_metric++;             # bump up the name
  787.     local(*metrics) = $next_metric; local($char);
  788.     while (<BDF>) {
  789.         next unless /^STARTCHAR/../^ENDCHAR/;
  790.         if (/^ENCODING\s+(\d+)/) { $char = $1; }
  791.         elsif (/^DWIDTH\s+(\d+)/)   { $metrics{$char}=$1; }
  792.     }
  793.     close(BDF);
  794.     
  795.     # Remember the name of the metrics array and return it
  796.     return $_metricsArrays{$key} = $next_metric;
  797. }
  798.  
  799. # Ugly stuff that I want to hide at the bottom
  800.  
  801. # For the purposes of mapping from quickdraw fonts to X11fonts, we define
  802. # the following dictionary:
  803. %font_metric_files = (
  804.                       "22 8 1","$X11FONTS/courB08.bdf",
  805.                       "22 10 1","$X11FONTS/courB10.bdf",
  806.                       "22 12 1","$X11FONTS/courB12.bdf",
  807.                       "22 14 1","$X11FONTS/courB14.bdf",
  808.                       "22 18 1","$X11FONTS/courB18.bdf",
  809.                       "22 24 1","$X11FONTS/courB24.bdf",
  810.                       "22 8 2","$X11FONTS/courO08.bdf",
  811.                       "22 10 2","$X11FONTS/courO10.bdf",
  812.                       "22 12 2","$X11FONTS/courO12.bdf",
  813.                       "22 14 2","$X11FONTS/courO14.bdf",
  814.                       "22 18 2","$X11FONTS/courO18.bdf",
  815.                       "22 24 2","$X11FONTS/courO24.bdf",
  816.                       "22 8 0","$X11FONTS/courR08.bdf",
  817.                       "22 10 0","$X11FONTS/courR10.bdf",
  818.                       "22 12 0","$X11FONTS/courR12.bdf",
  819.                       "22 14 0","$X11FONTS/courR14.bdf",
  820.                       "22 18 0","$X11FONTS/courR18.bdf",
  821.                       "22 24 0","$X11FONTS/courR24.bdf",
  822.                       "21 8 1","$X11FONTS/helvB08.bdf",
  823.                       "21 10 1","$X11FONTS/helvB10.bdf",
  824.                       "21 12 1","$X11FONTS/helvB12.bdf",
  825.                       "21 14 1","$X11FONTS/helvB14.bdf",
  826.                       "21 18 1","$X11FONTS/helvB18.bdf",
  827.                       "21 24 1","$X11FONTS/helvB24.bdf",
  828.                       "21 8 2","$X11FONTS/helvO08.bdf",
  829.                       "21 10 2","$X11FONTS/helvO10.bdf",
  830.                       "21 12 2","$X11FONTS/helvO12.bdf",
  831.                       "21 14 2","$X11FONTS/helvO14.bdf",
  832.                       "21 18 2","$X11FONTS/helvO18.bdf",
  833.                       "21 24 2","$X11FONTS/helvO24.bdf",
  834.                       "21 8 0","$X11FONTS/helvR08.bdf",
  835.                       "21 10 0","$X11FONTS/helvR10.bdf",
  836.                       "21 12 0","$X11FONTS/helvR12.bdf",
  837.                       "21 14 0","$X11FONTS/helvR14.bdf",
  838.                       "21 18 0","$X11FONTS/helvR18.bdf",
  839.                       "21 24 0","$X11FONTS/helvR24.bdf",
  840.                       "20 8 1","$X11FONTS/timB08.bdf",
  841.                       "20 10 1","$X11FONTS/timB10.bdf",
  842.                       "20 12 1","$X11FONTS/timB12.bdf",
  843.                       "20 14 1","$X11FONTS/timB14.bdf",
  844.                       "20 18 1","$X11FONTS/timB18.bdf",
  845.                       "20 24 1","$X11FONTS/timB24.bdf",
  846.                       "20 8 3","$X11FONTS/timBI08.bdf",
  847.                       "20 10 3","$X11FONTS/timBI10.bdf",
  848.                       "20 12 3","$X11FONTS/timBI12.bdf",
  849.                       "20 14 3","$X11FONTS/timBI14.bdf",
  850.                       "20 18 3","$X11FONTS/timBI18.bdf",
  851.                       "20 24 3","$X11FONTS/timBI24.bdf",
  852.                       "20 8 2","$X11FONTS/timI08.bdf",
  853.                       "20 10 2","$X11FONTS/timI10.bdf",
  854.                       "20 12 2","$X11FONTS/timI12.bdf",
  855.                       "20 14 2","$X11FONTS/timI14.bdf",
  856.                       "20 18 2","$X11FONTS/timI18.bdf",
  857.                       "20 24 2","$X11FONTS/timI24.bdf",
  858.                       "20 8 0","$X11FONTS/timR08.bdf",
  859.                       "20 10 0","$X11FONTS/timR10.bdf",
  860.                       "20 12 0","$X11FONTS/timR12.bdf",
  861.                       "20 14 0","$X11FONTS/timR14.bdf",
  862.                       "20 18 0","$X11FONTS/timR18.bdf",
  863.                       "20 24 0","$X11FONTS/timR24.bdf",
  864.                       "34 8 1","$X11FONTS/ncenB08.bdf",
  865.                       "34 10 1","$X11FONTS/ncenB10.bdf",
  866.                       "34 12 1","$X11FONTS/ncenB12.bdf",
  867.                       "34 14 1","$X11FONTS/ncenB14.bdf",
  868.                       "34 18 1","$X11FONTS/ncenB18.bdf",
  869.                       "34 24 1","$X11FONTS/ncenB24.bdf",
  870.                       "34 8 3","$X11FONTS/ncenBI08.bdf",
  871.                       "34 10 3","$X11FONTS/ncenBI10.bdf",
  872.                       "34 12 3","$X11FONTS/ncenBI12.bdf",
  873.                       "34 14 3","$X11FONTS/ncenBI14.bdf",
  874.                       "34 18 3","$X11FONTS/ncenBI18.bdf",
  875.                       "34 24 3","$X11FONTS/ncenBI24.bdf",
  876.                       "34 8 2","$X11FONTS/ncenI08.bdf",
  877.                       "34 10 2","$X11FONTS/ncenI10.bdf",
  878.                       "34 12 2","$X11FONTS/ncenI12.bdf",
  879.                       "34 14 2","$X11FONTS/ncenI14.bdf",
  880.                       "34 18 2","$X11FONTS/ncenI18.bdf",
  881.                       "34 24 2","$X11FONTS/ncenI24.bdf",
  882.                       "34 8 0","$X11FONTS/ncenR08.bdf",
  883.                       "34 10 0","$X11FONTS/ncenR10.bdf",
  884.                       "34 12 0","$X11FONTS/ncenR12.bdf",
  885.                       "34 14 0","$X11FONTS/ncenR14.bdf",
  886.                       "34 18 0","$X11FONTS/ncenR18.bdf",
  887.                       "34 24 0","$X11FONTS/ncenR24.bdf"
  888.                       );
  889. $next_metric = "metrics0000";   # name of our metrics arrays - dynamically allocated
  890.  
  891. 1;
  892. }       #end of package qd
  893.  
  894.