home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pdflb302.zip / pdf / pdflib-3.0.2 / pdflib / p_draw.c < prev    next >
C/C++ Source or Header  |  2000-08-21  |  12KB  |  466 lines

  1. /*---------------------------------------------------------------------------*
  2.  |        PDFlib - A library for dynamically generating PDF documents        |
  3.  +---------------------------------------------------------------------------+
  4.  |        Copyright (c) 1997-2000 Thomas Merz. All rights reserved.          |
  5.  +---------------------------------------------------------------------------+
  6.  |    This software is NOT in the public domain.  It can be used under two   |
  7.  |    substantially different licensing terms:                               |
  8.  |                                                                           |
  9.  |    The commercial license is available for a fee, and allows you to       |
  10.  |    - ship a commercial product based on PDFlib                            |
  11.  |    - implement commercial Web services with PDFlib                        |
  12.  |    - distribute (free or commercial) software when the source code is     |
  13.  |      not made available                                                   |
  14.  |    Details can be found in the file PDFlib-license.pdf.                   |
  15.  |                                                                           |
  16.  |    The "Aladdin Free Public License" doesn't require any license fee,     |
  17.  |    and allows you to                                                      |
  18.  |    - develop and distribute PDFlib-based software for which the complete  |
  19.  |      source code is made available                                        |
  20.  |    - redistribute PDFlib non-commercially under certain conditions        |
  21.  |    - redistribute PDFlib on digital media for a fee if the complete       |
  22.  |      contents of the media are freely redistributable                     |
  23.  |    Details can be found in the file aladdin-license.pdf.                  |
  24.  |                                                                           |
  25.  |    These conditions extend to ports to other programming languages.       |
  26.  |    PDFlib is distributed with no warranty of any kind. Commercial users,  |
  27.  |    however, will receive warranty and support statements in writing.      |
  28.  *---------------------------------------------------------------------------*/
  29.  
  30. /* p_draw.c
  31.  *
  32.  * PDFlib drawing routines
  33.  *
  34.  */
  35.  
  36. #include <math.h>
  37.  
  38. #include "p_intern.h"
  39.  
  40. /* Path segment operators */
  41.  
  42. static void
  43. pdf_begin_path(PDF *p)
  44. {
  45.     if (p->contents == c_path)
  46.     return;
  47.  
  48.     pdf_end_text(p);
  49.  
  50.     p->contents = c_path;
  51. }
  52.  
  53. void
  54. pdf_end_path(PDF *p, pdf_bool force_endpath)
  55. {
  56.     if (p->contents != c_path)
  57.     return;
  58.  
  59.     if (force_endpath)
  60.     pdf_puts(p, "n\n");    /* force an endpath if the caller forgot it */
  61.  
  62.     p->contents = c_page;
  63.  
  64.     p->gstate[p->sl].x = (float) 0.0;
  65.     p->gstate[p->sl].y = (float) 0.0;
  66. }
  67.  
  68. PDFLIB_API void PDFLIB_CALL
  69. PDF_moveto(PDF *p, float x, float y)
  70. {
  71.     char buf1[FLOATBUFSIZE], buf2[FLOATBUFSIZE];
  72.  
  73.     if (PDF_SANITY_CHECK_FAILED(p))
  74.     return;
  75.  
  76.     /* starting a new path */
  77.     if (p->contents != c_path) {
  78.     p->gstate[p->sl].startx = x;
  79.     p->gstate[p->sl].starty = y;
  80.     }
  81.  
  82.     pdf_begin_path(p);
  83.  
  84.     pdf_printf(p, "%s %s m\n", pdf_float(buf1, x), pdf_float(buf2, y));
  85.  
  86.     p->gstate[p->sl].x = x;
  87.     p->gstate[p->sl].y = y;
  88. }
  89.  
  90. PDFLIB_API void PDFLIB_CALL
  91. PDF_lineto(PDF *p, float x, float y)
  92. {
  93.     char buf1[FLOATBUFSIZE], buf2[FLOATBUFSIZE];
  94.  
  95.     if (PDF_SANITY_CHECK_FAILED(p))
  96.     return;
  97.  
  98.     pdf_begin_path(p);
  99.  
  100.     pdf_printf(p, "%s %s l\n", pdf_float(buf1, x), pdf_float(buf2, y));
  101.  
  102.     p->gstate[p->sl].x = x;
  103.     p->gstate[p->sl].y = y;
  104. }
  105.  
  106. PDFLIB_API void PDFLIB_CALL
  107. PDF_curveto(PDF *p, float x1, float y1, float x2, float y2, float x3, float y3)
  108. {
  109.     char buf1[FLOATBUFSIZE], buf2[FLOATBUFSIZE];
  110.     char buf3[FLOATBUFSIZE], buf4[FLOATBUFSIZE];
  111.     char buf5[FLOATBUFSIZE], buf6[FLOATBUFSIZE];
  112.  
  113.     if (PDF_SANITY_CHECK_FAILED(p))
  114.     return;
  115.  
  116.     /* starting a new path */
  117.     if (p->contents != c_path) {
  118.     p->gstate[p->sl].startx = x1;
  119.     p->gstate[p->sl].starty = y1;
  120.     }
  121.  
  122.     pdf_begin_path(p);
  123.  
  124.     if (x2 == x3 && y2 == y3)    /* second c.p. coincides with final point */
  125.     pdf_printf(p, "%s %s %s %s y\n",
  126.         pdf_float(buf1, x1), pdf_float(buf2, y1),
  127.         pdf_float(buf3, x3), pdf_float(buf4, y3));
  128.     else            /* general case with four distinct points */
  129.     pdf_printf(p, "%s %s %s %s %s %s c\n",
  130.         pdf_float(buf1, x1), pdf_float(buf2, y1),
  131.         pdf_float(buf3, x2), pdf_float(buf4, y2),
  132.         pdf_float(buf5, x3), pdf_float(buf6, y3));
  133.  
  134.     p->gstate[p->sl].x = x3;
  135.     p->gstate[p->sl].y = y3;
  136. }
  137.  
  138. #define ARC_MAGIC    ((float) 0.5523)
  139. #define MIN(x, y)    ((x) < (y) ? (x) : (y))
  140.  
  141. static void
  142. pdf_arc_internal(PDF *p, float x, float y, float r, float alpha, float beta, pdf_bool moveto)
  143. {
  144.     float bcp;
  145.     float cos_alpha, cos_beta, sin_alpha, sin_beta;
  146.     float startx, starty;
  147.  
  148.     /*
  149.      * pdf_begin_path() not required since we descend to another
  150.      * path segment function in all cases.
  151.      */
  152.  
  153.     /* The starting point */
  154.     startx = (float) (x + r * cos(M_PI * alpha / 180.0));
  155.     starty = (float) (y + r * sin(M_PI * alpha / 180.0));
  156.  
  157.     /* move to the beginning of the arc if requested, and if not already there */
  158.     if (moveto && (p->gstate[p->sl].x != startx || p->gstate[p->sl].y != starty))
  159.     PDF_moveto(p, startx, starty);
  160.  
  161.     if (beta - alpha > 90.0) {
  162.     /* cut down in 90 degree segments until done */
  163.     pdf_arc_internal(p, x, y, r, alpha, MIN(alpha + 90, beta), pdf_false);
  164.     if (alpha + 90 < beta)
  165.         pdf_arc_internal(p, x, y, r, alpha + 90, beta, pdf_false);
  166.     return;
  167.     }
  168.  
  169.     /* speed up special case for quadrants and circles */
  170.     if ((int) alpha == alpha && (int) beta == beta &&
  171.         beta - alpha == 90 && (int) alpha % 90 == 0)
  172.     {
  173.     /* prune angle values */
  174.     alpha = (float) ((int) alpha % 360);
  175.  
  176.     switch ((int) alpha) {
  177.         case 0:
  178.         PDF_curveto(p, x + r, y + r*ARC_MAGIC, 
  179.             x + r*ARC_MAGIC, y + r, x, y + r);
  180.         break;
  181.         case 90:
  182.         PDF_curveto(p, x - r*ARC_MAGIC, y + r, 
  183.             x - r, y + r*ARC_MAGIC, x - r, y);
  184.         break;
  185.         case 180:
  186.         PDF_curveto(p, x - r, y - r*ARC_MAGIC, 
  187.             x - r*ARC_MAGIC, y - r, x, y - r);
  188.         break;
  189.         case 270:
  190.         PDF_curveto(p, x + r*ARC_MAGIC, y - r, 
  191.             x + r, y - r*ARC_MAGIC, x + r, y);
  192.         break;
  193.         default:
  194.         break;
  195.     }
  196.     return;
  197.     }
  198.  
  199.     alpha = (float) (alpha * M_PI / 180);    /* convert to radians */
  200.     beta  = (float) (beta * M_PI / 180);    /* convert to radians */
  201.  
  202.     /* This formula yields ARC_MAGIC for alpha == 0, beta == 90 degrees */
  203.     bcp = (float) (4.0/3 * (1 - cos((beta - alpha)/2)) / sin((beta - alpha)/2));
  204.     
  205.     sin_alpha = (float) sin(alpha);
  206.     sin_beta = (float) sin(beta);
  207.     cos_alpha = (float) cos(alpha);
  208.     cos_beta = (float) cos(beta);
  209.  
  210.     /* new endpoint */
  211.     p->gstate[p->sl].x = x + r * cos_beta;
  212.     p->gstate[p->sl].y = y + r * sin_beta;
  213.  
  214.     PDF_curveto(p, 
  215.         x + r * (cos_alpha - bcp * sin_alpha),        /* p1 */
  216.         y + r * (sin_alpha + bcp * cos_alpha),
  217.         x + r * (cos_beta + bcp * sin_beta),        /* p2 */
  218.         y + r * (sin_beta - bcp * cos_beta),
  219.         p->gstate[p->sl].x,                /* p3 */
  220.         p->gstate[p->sl].y);
  221. }
  222.  
  223. PDFLIB_API void PDFLIB_CALL
  224. PDF_arc(PDF *p, float x, float y, float r, float alpha, float beta)
  225. {
  226.     if (PDF_SANITY_CHECK_FAILED(p))
  227.     return;
  228.  
  229.     if (r < 0)
  230.     pdf_error(p, PDF_ValueError, "Negative arc radius %f", r);
  231.     
  232.     pdf_arc_internal(p, x, y, r, alpha, beta, pdf_true);
  233. }
  234.  
  235. PDFLIB_API void PDFLIB_CALL
  236. PDF_circle(PDF *p, float x, float y, float r)
  237. {
  238.     if (PDF_SANITY_CHECK_FAILED(p))
  239.     return;
  240.  
  241.     if (r < 0)
  242.     pdf_error(p, PDF_ValueError, "Negative circle radius %f", r);
  243.  
  244.     /*
  245.      * pdf_begin_path() not required since we descend to other
  246.      * path segment functions.
  247.      */
  248.  
  249.     /* draw four Bezier curves to approximate a circle */
  250.     PDF_moveto(p, x + r, y);
  251.     PDF_curveto(p, x + r, y + r*ARC_MAGIC, x + r*ARC_MAGIC, y + r, x, y + r);
  252.     PDF_curveto(p, x - r*ARC_MAGIC, y + r, x - r, y + r*ARC_MAGIC, x - r, y);
  253.     PDF_curveto(p, x - r, y - r*ARC_MAGIC, x - r*ARC_MAGIC, y - r, x, y - r);
  254.     PDF_curveto(p, x + r*ARC_MAGIC, y - r, x + r, y - r*ARC_MAGIC, x + r, y);
  255.  
  256.     p->gstate[p->sl].x = x + r;
  257.     p->gstate[p->sl].y = y;
  258. }
  259.  
  260. PDFLIB_API void PDFLIB_CALL
  261. PDF_rect(PDF *p, float x, float y, float width, float height)
  262. {
  263.     char buf1[FLOATBUFSIZE], buf2[FLOATBUFSIZE];
  264.     char buf3[FLOATBUFSIZE], buf4[FLOATBUFSIZE];
  265.  
  266.     if (PDF_SANITY_CHECK_FAILED(p))
  267.     return;
  268.  
  269.     /* starting a new path */
  270.     if (p->contents != c_path) {
  271.     p->gstate[p->sl].startx = x;
  272.     p->gstate[p->sl].starty = y;
  273.     }
  274.  
  275.     pdf_begin_path(p);
  276.  
  277.     pdf_printf(p, "%s %s %s %s re\n",
  278.             pdf_float(buf1, x), pdf_float(buf2, y),
  279.             pdf_float(buf3, width), pdf_float(buf4, height));
  280.  
  281.     p->gstate[p->sl].x = x;
  282.     p->gstate[p->sl].y = y;
  283. }
  284.  
  285. PDFLIB_API void PDFLIB_CALL
  286. PDF_closepath(PDF *p)
  287. {
  288.     if (PDF_SANITY_CHECK_FAILED(p))
  289.     return;
  290.  
  291.     /* This may look strange but is correct */
  292.     pdf_begin_path(p);
  293.  
  294.     pdf_puts(p, "h\n");
  295.  
  296.     p->gstate[p->sl].x = p->gstate[p->sl].startx;
  297.     p->gstate[p->sl].y = p->gstate[p->sl].starty;
  298. }
  299.  
  300. /* Path painting operators */
  301.  
  302. PDFLIB_API void PDFLIB_CALL
  303. PDF_endpath(PDF *p)
  304. {
  305.     if (PDF_SANITY_CHECK_FAILED(p))
  306.     return;
  307.  
  308.     if (p->contents != c_path) {
  309.     pdf_error(p, PDF_NonfatalError, "No path available in PDF_endpath");
  310.     return;
  311.     }
  312.  
  313.     pdf_end_path(p, pdf_true);
  314. }
  315.  
  316. PDFLIB_API void PDFLIB_CALL
  317. PDF_stroke(PDF *p)
  318. {
  319.     if (PDF_SANITY_CHECK_FAILED(p))
  320.     return;
  321.  
  322.     if (p->contents != c_path) {
  323.     pdf_error(p, PDF_NonfatalError, "No path available in PDF_stroke");
  324.     return;
  325.     }
  326.  
  327.     pdf_puts(p, "S\n");
  328.  
  329.     pdf_end_path(p, pdf_false);
  330. }
  331.  
  332. PDFLIB_API void PDFLIB_CALL
  333. PDF_closepath_stroke(PDF *p)
  334. {
  335.     if (PDF_SANITY_CHECK_FAILED(p))
  336.     return;
  337.  
  338.     if (p->contents != c_path) {
  339.     pdf_error(p, PDF_NonfatalError,
  340.         "No path available in PDF_closepath_stroke");
  341.     return;
  342.     }
  343.  
  344.     pdf_puts(p, "s\n");
  345.  
  346.     pdf_end_path(p, pdf_false);
  347. }
  348.  
  349. PDFLIB_API void PDFLIB_CALL
  350. PDF_fill(PDF *p)
  351. {
  352.     if (PDF_SANITY_CHECK_FAILED(p))
  353.     return;
  354.  
  355.     if (p->contents != c_path) {
  356.     pdf_error(p, PDF_NonfatalError, "No path available in PDF_fill");
  357.     return;
  358.     }
  359.  
  360.     switch (p->fillrule) {
  361.     case pdf_fill_winding:
  362.         pdf_puts(p, "f\n");
  363.         break;
  364.  
  365.     case pdf_fill_evenodd:
  366.         pdf_puts(p, "f*\n");
  367.         break;
  368.  
  369.     default:
  370.         pdf_error(p, PDF_RuntimeError,
  371.         "Inconsistent fill rule value in PDF_fill");
  372.     }
  373.  
  374.     pdf_end_path(p, pdf_false);
  375. }
  376.  
  377. PDFLIB_API void PDFLIB_CALL
  378. PDF_fill_stroke(PDF *p)
  379. {
  380.     if (PDF_SANITY_CHECK_FAILED(p))
  381.     return;
  382.  
  383.     if (p->contents != c_path) {
  384.     pdf_error(p, PDF_NonfatalError, "No path available in PDF_fill_stroke");
  385.     return;
  386.     }
  387.  
  388.     switch (p->fillrule) {
  389.     case pdf_fill_winding:
  390.         pdf_puts(p, "B\n");
  391.         break;
  392.  
  393.     case pdf_fill_evenodd:
  394.         pdf_puts(p, "B*\n");
  395.         break;
  396.  
  397.     default:
  398.         pdf_error(p, PDF_RuntimeError,
  399.         "Inconsistent fill rule value in PDF_fill_stroke");
  400.     }
  401.  
  402.     pdf_end_path(p, pdf_false);
  403. }
  404.  
  405. PDFLIB_API void PDFLIB_CALL
  406. PDF_closepath_fill_stroke(PDF *p)
  407. {
  408.     if (PDF_SANITY_CHECK_FAILED(p))
  409.     return;
  410.  
  411.     if (p->contents != c_path) {
  412.     pdf_error(p, PDF_NonfatalError,
  413.         "No path available in PDF_closepath_fill_stroke");
  414.     return;
  415.     }
  416.  
  417.     switch (p->fillrule) {
  418.     case pdf_fill_winding:
  419.     pdf_puts(p, "b\n");
  420.     break;
  421.  
  422.     case pdf_fill_evenodd:
  423.     pdf_puts(p, "b*\n");
  424.     break;
  425.  
  426.     default:
  427.     pdf_error(p, PDF_RuntimeError,
  428.         "Inconsistent fill rule value in PDF_closepath_fill_stroke");
  429.     }
  430.  
  431.     pdf_end_path(p, pdf_false);
  432. }
  433.  
  434. /* Path clipping operators */
  435.  
  436. PDFLIB_API void PDFLIB_CALL
  437. PDF_clip(PDF *p)
  438. {
  439.     if (PDF_SANITY_CHECK_FAILED(p))
  440.     return;
  441.  
  442.     if (p->contents != c_path) {
  443.     pdf_error(p, PDF_NonfatalError, "No path available in PDF_clip");
  444.     return;
  445.     }
  446.  
  447.     pdf_begin_path(p);
  448.  
  449.     switch (p->fillrule) {
  450.     case pdf_fill_winding:
  451.     pdf_puts(p, "W n\n");
  452.  
  453.     break;
  454.     case pdf_fill_evenodd:
  455.     pdf_puts(p, "W* n\n");
  456.     break;
  457.  
  458.     default:
  459.     pdf_error(p, PDF_RuntimeError,
  460.         "Inconsistent fill rule value in PDF_clip");
  461.     return;
  462.     }
  463.  
  464.     pdf_end_path(p, pdf_false);
  465. }
  466.