//=========================================================
//  MusE
//  Linux Music Editor
//    $Id: print.cpp,v 1.1.1.1 2003/10/29 10:05:29 wschweer Exp $
//  (C) Copyright 1999,2000 Werner Schweer (ws@seh.de)
//=========================================================

#include "config.h"

#include "ncanvas.h"
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>

#include "conf.h"
#include "layout.h"
#include "globals.h"
#include "print.h"
#include "symbols.h"
#include "glyphs.h"
#include "event.h"
#include "song.h"

PsStatus pStatus;
extern char psFont[];

double systemLineWidth = 0.4;
double barLineWidth    = 1.0;
double stemLineWidth   = .4;

//
//  from QT2.1
//
static const struct {
      const char * input;
      const char * roman;
      const char * italic;
      const char * bold;
      const char * boldItalic;
      const char * light;
      const char * lightItalic;
      } postscriptFontNames[] = {
      { "arial", "Arial", 0, 0, 0, 0, 0 },
      { "avantgarde", "AvantGarde-Book", 0, 0, 0, 0, 0 },
      { "charter", "CharterBT-Roman", 0, 0, 0, 0, 0 },
      { "garamond", "Garamond-Regular", 0, 0, 0, 0, 0 },
      { "gillsans", "GillSans", 0, 0, 0, 0, 0 },
      { "helvetica",
        "Helvetica", "Helvetica-Oblique",
        "Helvetica-Bold", "Helvetica-BoldOblique",
        "Helvetica", "Helvetica-Oblique" },
      { "new century schoolbook", "NewCenturySchlbk-Roman", 0, 0, 0, 0, 0 },
      { "symbol", "Symbol", "Symbol", "Symbol", "Symbol", "Symbol", "Symbol" },
      { "terminal", "Courier", 0, 0, 0, 0, 0 },
      { "times new roman", "TimesNewRoman", 0, 0, 0, 0, 0 },
      { "utopia", "Utopia-Regular", 0, 0, 0, 0, 0 },
      { 0, 0, 0, 0, 0, 0, 0 }
      };

PsStatus::PsStatus() {
      }

//---------------------------------------------------------
//   PsStatus::init
//---------------------------------------------------------

void PsStatus::init()
      {
      curLineWidth  = 0.0;
      fontFlag = 0;
      for (int i = 0; i < MAX_GLYPHS; ++i) {
            glyphExists[i] = false;
            }
      }

//---------------------------------------------------------
//   setFont
//    parts stolen from QT2.1
//---------------------------------------------------------

void PsStatus::setFont(FILE* f, const QFont* nf)
      {
      if (nf == 0) {
            if (fontFlag == 1)
                  return;
            fprintf(f, "NF setfont\n");
            fontFlag = 1;
            return;
            }
      if ((fontFlag == 2) && (nf->family() == font.family())
         && (nf->pointSize() == font.pointSize())
         && (nf->weight() == font.weight())
         && (nf->italic() == font.italic())
         && (nf->underline() == font.underline()))
            return;

      QString family = nf->family();
      int	 weight  = nf->weight();
      bool italic    = nf->italic();

      family = family.lower();

      // try to make a "good" postscript name
      QString ps = family.simplifyWhiteSpace();
      int i = 0;
      while( (unsigned int)i < ps.length() ) {
            if (i == 0 || ps[i-1] == ' ') {
                  ps[i] = ps[i].upper();
                  if (i)
                        ps.remove( i-1, 1 );
                  else
                        i++;
                  }
            else {
                  i++;
                  }
            }

      // see if the table has a better name
      i = 0;
      while (postscriptFontNames[i].input &&
         QString::fromLatin1(postscriptFontNames[i].input) != family)
            i++;
      if (postscriptFontNames[i].roman) {
            ps = QString::fromLatin1(postscriptFontNames[i].roman);
            int p = ps.find( '-' );
            if ( p > -1 )
                  ps.truncate( p );
            }

      // get the right modification, or build something
      if (weight >= QFont::Bold && italic) {
            if (postscriptFontNames[i].boldItalic)
                  ps = QString::fromLatin1(postscriptFontNames[i].boldItalic);
            else
                  ps.append( QString::fromLatin1("-BoldItalic") );
            }
      else if (weight >= QFont::Bold) {
            if (postscriptFontNames[i].bold)
                  ps = QString::fromLatin1(postscriptFontNames[i].bold);
            else
                  ps.append( QString::fromLatin1("-Bold"));
            }
      else if (weight >= QFont::DemiBold && italic) {
            if ( postscriptFontNames[i].italic )
                  ps = QString::fromLatin1(postscriptFontNames[i].italic);
            else
                  ps.append( QString::fromLatin1("-Italic"));
            }
      else if (weight <= QFont::Light && italic) {
            if ( postscriptFontNames[i].lightItalic )
                  ps = QString::fromLatin1(postscriptFontNames[i].lightItalic);
            else
                  ps.append( QString::fromLatin1("-LightItalic") );
            }
      else if (weight <= QFont::Light) {
            if ( postscriptFontNames[i].light)
                  ps = QString::fromLatin1(postscriptFontNames[i].light);
            else
                  ps.append(QString::fromLatin1("-Light"));
            }
      else if ( italic ) {
            if ( postscriptFontNames[i].italic )
                  ps = QString::fromLatin1(postscriptFontNames[i].italic);
            else
                  ps.append( QString::fromLatin1("-Italic") );
            }
      else {
            if ( postscriptFontNames[i].roman )
                  ps = QString::fromLatin1(postscriptFontNames[i].roman);
            else
                  ps.append( QString::fromLatin1("-Roman") );
            }
      fprintf(f, "[%d 0 0 -%d 0 0] ", nf->pointSize(), nf->pointSize());
      fprintf(f, "/%s /%s ChgFnt\n", ps.ascii(), ps.ascii());
      font = *nf;
      fontFlag = 2;
      }

//---------------------------------------------------------
//   setLineWidth
//---------------------------------------------------------

void PsStatus::setLineWidth(FILE* f, double w)
      {
      if (curLineWidth != w) {
            fprintf(f, "%.3f setlinewidth\n", w);
            }
      curLineWidth = w;
      }

//---------------------------------------------------------
//   Text:print
//---------------------------------------------------------

void Text::print(FILE* f) const
      {
      pStatus.setFont(f, &font);
      fprintf(f, "%d %d moveto ", pt.x(), pt.y());
      if (align & QPainter::AlignRight) {
            fprintf(f, "(%s) dup stringwidth pop 0 exch sub 0 rmoveto",
               s.ascii());
            }
      else if (align & QPainter::AlignHCenter) {
            fprintf(f, "(%s) dup stringwidth pop 0 exch 2 div sub 0 rmoveto",
               s.ascii());
            }
      else {
            fprintf(f, "(%s)", s.ascii());      // align left
            }
      fprintf(f, " show\n");
      }

//---------------------------------------------------------
//   LyricsItem:print
//---------------------------------------------------------

void LyricsItem::print(FILE* f) const
      {
      QPoint rp = pos() + _note->pos();
      pStatus.setFont(f, &font);
      fprintf(f, "%d %d moveto ", rp.x(), rp.y());
      fprintf(f, "(%s) dup stringwidth pop 2 div 0 exch sub 0 rmoveto",
//      fprintf(f, "(%s) dup stringwidth pop 0 exch sub 0 rmoveto",
         s.ascii());
      fprintf(f, " show\n");
      }

//---------------------------------------------------------
//   Clef::print
//---------------------------------------------------------

void Clef::print(FILE* f) const
      {
      int val = scale->idx();
      if (val == 0)
            return;

      pStatus.setFont(f, 0);
      int x = pt.x();
      if (val > 0) {
            for (int i = 0; i < val; ++i) {
                  int k = sharpTab[key->idx()][i];
                  fprintf(f, "%d %d moveto <cb> show\n", x + i*6, k * 3);
                  }
            }
      else {
            int s = -val;
            for (int i = 0; i < s; ++i) {
                  int k = flatTab[key->idx()][i];
                  fprintf(f, "%d %d moveto <c8> show\n", x + i*6, k * 3);  // flat
                  }
            }
      }

//---------------------------------------------------------
//   print
//---------------------------------------------------------

void SystemItem::print(FILE* f) const
      {
      pStatus.setLineWidth(f, systemLineWidth);
      fprintf(f, "newpath ");
      for (int k = 0; k < lines; ++k)
            fprintf(f, "0 %d moveto %d %d lineto\n",
               k*6, width, k*6);
      fprintf(f, "stroke\n");
      }

//---------------------------------------------------------
//   MeasureItem:print
//---------------------------------------------------------

void MeasureItem::print(FILE* f) const
      {
      double x = double(pt.x());
      double y = double(pt.y());

      pStatus.setLineWidth(f, barLineWidth);
      switch(type) {
            case 1:
                  fprintf(f, "newpath %.2f %.2f moveto %.2f %.2f lineto stroke\n",
                     x-4.6, y, x-4.6, y + h);
                  fprintf(f,
                     "newpath %.2f %.2f moveto "
                     "%.2f %.2f lineto "
                     "%.2f %.2f lineto "
                     "%.2f %.2f lineto "
                     "%.2f %.2f lineto fill\n",
                     x-2.3, y,
                     x,     y,
                     x,   y+h,
                     x-2.3, y+h,
                     x - 2.3, y);
                  break;
            case 0:
            default:
                  fprintf(f, "newpath %.2f %.2f moveto %.2f %.2f lineto stroke\n",
                     x, y, x, y + h);
                  break;
            }
      }

//---------------------------------------------------------
//   NoteItem::print
//---------------------------------------------------------

void NoteItem::print(FILE* f) const
      {
      pStatus.setFont(f, 0);

      double x = double(pt.x());
      double y = _hline * 3.0;

      //-------------------------------
      //  Note Head
      //-------------------------------

      switch (_head) {
            case 0:  // no head
                  break;
            case 1:  // 1/4
                  fprintf(f, "%.2f %.2f moveto <a8> show\n", x-3.75, y);
                  break;
            case 2:  // 2/4
                  fprintf(f, "%.2f %.2f moveto <a9> show\n", x-3.75, y);
                  break;
            case 3:  // 4/4
                  fprintf(f, "%.2f %.2f moveto <aa> show\n", x-3.75, y);
                  break;
            default: assert(false); break;
            }
      //
      //-------------------------------
      //  Versetzungszeichen
      //-------------------------------
      //

      int vz = 0;

      switch (_prefix) {
            case 0: break;
            case 1: vz = 0xcb; break;
            case 2: vz = 0xc8; break;
            case 3: vz = 0xcd; break;
            case 4: vz = 0xca; break;
            case 5: vz = 0xce; break;
            default: assert(false); break;
            }
      if (vz)
            fprintf(f, "%.2f %.2f moveto <%02x> show\n", x-13.75, y, vz);

      double sw = _stem > 0 ? 3.75 : - 3.75;
      //
      //-------------------------------
      //  Stem
      //-------------------------------
      //
      if (_stem) {
            pStatus.setLineWidth(f, stemLineWidth);
            fprintf(f, "newpath\n");
            fprintf(f, "newpath %.2f %.2f moveto\n", x + sw, y);
            fprintf(f, "0 %d rlineto stroke\n", -_stem);
            }

      //
      //-------------------------------
      //  Hilfslinien
      //-------------------------------
      //

      if ((_hline <= -2) || (_hline >= 10)) {
            pStatus.setLineWidth(f, systemLineWidth);
            fprintf(f, "newpath\n");
            for (int l = -2; l >= _hline; l -= 2) {
                  fprintf(f, "%.2f %.2f moveto\n", x-5.75, l*3.0);
                  fprintf(f, "11.5 0 rlineto\n");
                  }
            for (int l = 10; l <= _hline; l += 2) {
                  fprintf(f, "%.2f %.2f moveto\n", x-5.75, l*3.0);
                  fprintf(f, "11.5 0 rlineto\n");
                  }
            fprintf(f, "stroke\n");
            }

      //
      //-----------------------------------------
      //  Fähnchen und Verlängerungszeichen
      //-----------------------------------------
      //

      int nn = _len/96;      // 16'tel
      switch (nn) {
            case 1:
                  if (_flags && _stem) {
                        if (_stem < 0)
                              fprintf(f, "%.2f %.2f moveto <2e> show\n", x+sw, y-_stem);
                        else
                              fprintf(f, "%.2f %.2f moveto <29> show\n", x+sw, y-_stem+20);
                  }
                  break;
            case 2:
                  if (_flags && _stem) {
                        if (_stem < 0)
                              fprintf(f, "%.2f %.2f moveto <2d> show\n", x+sw, y-_stem);
                        else
                              fprintf(f, "%.2f %.2f moveto <28> show\n", x+sw, y-_stem+20);
                        }
                  break;
            case 3:     // 1/8 + 1/16
                  if (_flags && _stem) {
                        if (_stem < 0)
                              fprintf(f, "%.2f %.2f moveto <2d> show\n", x+sw, y-_stem);
                        else
                              fprintf(f, "%.2f %.2f moveto <28> show\n", x+sw, y-_stem+20);
                        }
                  fprintf(f, "%.2f %.2f moveto <a1> show\n", x, y);  // dot
                  break;
            case 4:
            case 8:
                  break;
            case 6:
            case 12:
                  fprintf(f, "%.2f %.2f moveto <a1> show\n", x, y);  // dot
                  break;
            case 7:
                  fprintf(f, "%.2f %.2f moveto <a2> show\n", x, y);  // dot dot
                  break;
            case 14:
                  fprintf(f, "%.2f %.2f moveto <a2> show\n", x, y);  // dot dot
                  break;
            case 15:
                  fprintf(f, "%.2f %.2f moveto <a3> show\n", x, y);  // dot dot dot
                  break;
            case 5:
            case 9:
            case 10:
            case 11:
            case 13:
                  printf("kann Notenlänge %d nicht verarbeiten\n", nn);
                  break;
            }
      }


//---------------------------------------------------------
//   Tie::print
//---------------------------------------------------------

void Tie::print(FILE* f) const
      {
      QPointArray pa(4);

      double lt = 0.92;
      double cu = 2.88;

      QPoint p1 = note1->pos();
      QPoint p2 = note2->pos();
      if (note2->staveRow() != note1->staveRow()) {
            if (note2->staveRow() != sr) {
                  System* s = sr->system;
                  int x = (s->endBar-s->startBar)*s->measureWidth+s->xoffset;
                  p2 = QPoint(x+5, p1.y());
                  }
            else
                  p1 = p2 + QPoint(-15, 0);
            }

      double y1, y2, y3, y4;
      double y5, y6;
      int yo = note1->hline() * 3;
      if (_up) {
            y1 = yo - 4.0;
            y2 = y1 - cu;
            y3 = y1 - cu;
            y4 = y1;

            y5 = y2 - lt;
            y6 = y3 - lt;
            }
      else {
            y1 = yo + 4.0;
            y2 = y1 + cu;
            y3 = y1 + cu;
            y4 = y1;

            y5 = y2 + lt;
            y6 = y3 + lt;
            }
      int x1 = p1.x() + 3;
      int x4 = p2.x() - 3;
      int x2 = x1 + (x4-x1)/4;
      int x3 = x4 - (x4-x1)/4;

      fprintf(f, "newpath\n");
      fprintf(f, "%d %.2f moveto\n", x1, y1);
      fprintf(f, "%d %.2f %d %.2f %d %.2f curveto\n", x2, y2, x3, y3, x4, y4);
      fprintf(f, "%d %.2f %d %.2f %d %.2f curveto\n", x3, y6, x2, y5, x1, y1);
      fprintf(f, "fill\n");
      }

//---------------------------------------------------------
//   print
//---------------------------------------------------------

void SystemLayout::preview()
      {

      printf(" %d  %d\n", getuid(), geteuid());
      char tmp[32] = "/tmp/MusEXXXXXX";
      int fd  = mkstemp(tmp);
      if (fd == -1) {
            perror("mkstemp");
            }
      FILE* f = fdopen(fd, "w");
      if (f == 0) {
            printf("Cannot open/create tmp file <%s>: %s\n",
               tmp, strerror(errno));
            return;
            }
      print(f);
      fclose(f);

      int len = previewCommand.length() + strlen(tmp) + 1;
      char buffer[len];
      snprintf(buffer, len, previewCommand.ascii(), tmp);

      if (system(buffer) == -1)
            printf("system(%s) failed: %s\n", buffer, strerror(errno));
/*      if (remove(tmp) == -1) {
            printf("Cannot remove tmp file <%s>: %s\n",
               buffer, strerror(errno));
            }
      */
      }

//---------------------------------------------------------
//   print
//---------------------------------------------------------

void SystemLayout::print()
      {
      FILE* f;
      if (printerType == 1) {
            f = fopen(printerFile.ascii(), "w");
            if (f == 0) {
                  printf("Cannot open/create file <%s>: %s\n",
                     printerFile.ascii(), strerror(errno));
                  return;
                  }
            }
      else {
            f = popen(printerCommand.ascii(), "w");
            if (f == 0) {
                  printf("Cannot open/create pipe <%s>: %s\n",
                     printerCommand.ascii(), strerror(errno));
                  return;
                  }
            }
      print(f);
      if (printerType == 1)
            fclose(f);
      else
            pclose(f);
      }

//---------------------------------------------------------
//   print
//---------------------------------------------------------

void SystemLayout::print(FILE* f)
      {
      pStatus.init();
      fprintf(f, "%%!PS-Adobe-2.0\n");
      fprintf(f, "%%%%Creator: MusE %s\n", VERSION);
      fprintf(f, "%%%%Title: %s\n", song->name().ascii());
      fprintf(f, "%%%%Pages: %d\n", pages.size());
      fprintf(f, "%%%%PageOrder: Ascend\n");
      fprintf(f, "%%%%BoundingBox: 0 0 %d %d\n",
         int(paperWidth*72.0/25.4), int(paperHeight*72.0/25.4));

      fputs(psFont, f);
      fprintf(f, "%%%%BeginSetup\n");
      fprintf(f, "%%%%Feature: *Resolution 600dpi\n");
      fprintf(f, "%%%%PaperSize: A4\n");
//      fprintf(f, "[%.2f 0 0 %.2f 0 %d] concat\n",
//         printScale, -printScale, int(paperHeight*72.0/25.4));
      fprintf(f, "%%%%EndSetup\n");

      int page = 1;
      for (iPage i = pages.begin(); i != pages.end(); ++i) {
            curPage = &*i;
            fprintf(f, "%%%%Page: %d %d\n", curPage->pageno, page);
            ++page;

//DEBUG
      fprintf(f, "[%.2f 0 0 %.2f 0 %d] concat\n",
         printScale, -printScale, int(paperHeight*72.0/25.4));

            ScoreList* scl = &(curPage->items);
            for (iScore j = scl->begin(); j != scl->end(); ++j)
                  j->second->print(f);

            SystemList* sl = &curPage->systems;
            for (iSystem i = sl->begin(); i != sl->end(); ++i) {
                  fprintf(f, "\n%%System\n");
                  System* system = &*i;
                  StaveRowList* stl = &(system->staves);
                  for (iStaveRow k = stl->begin(); k != stl->end(); ++k) {
                        ScoreList* scl = &(k->items);
                        fprintf(f, "\n%%StaveRow %d(%d)\n", k->no, stl->size());
                        fprintf(f, "gsave %d %d translate\n", k->offset.x(), k->offset.y());
                        for (iScore j = scl->begin(); j != scl->end(); ++j)
                              j->second->print(f);
                        pStatus.fontFlag = 0;
                        fprintf(f, " grestore\n");
                        }
                  }
            fprintf(f, "showpage\n");
            }
      }

//---------------------------------------------------------
//   print
//---------------------------------------------------------

void KeyItem::print(FILE* f) const
      {
      pStatus.setFont(f, 0);
      fprintf(f, "%d ",  pt.x());
      switch (key->idx()) {
            case 0:
            case 1:   // +8
            case 2:   // +12
            case 3:   // -8
                  fprintf(f, "18 moveto (G)");
                  break;
            case 4:
            case 5:   // -8
            case 6:   // -15
                  fprintf(f, "6 moveto <49>");
                  break;
            case 9:
                  fprintf(f, "6 moveto <4b>");
                  break;
            case 13:
                  fprintf(f, "12 moveto <4d>");
                  break;
            default:
                  assert(false);
            }
      fprintf(f, " show\n");
      }

//---------------------------------------------------------
//   RestItem::print
//---------------------------------------------------------

void RestItem::print(FILE* f) const
      {
      pStatus.setFont(f, 0);
      int r;
      int y = 0;
      if (pm == restBM[0]) {
            y = 9;
            r = 0x3c;
            }
      else {
            int nn = _len/24;        // 1/64
            switch (nn) {
                  case 1:  r = 0x42; y = 25; break;  // 1/64
                  case 2:  r = 0x41; y = 25; break;  // 1/32
                  case 4:  r = 0x40; y = 25; break;  // 1/16
                  case 8:  r = 0x3f; y = 25; break;  // 1/8
                  case 16: r = 0x3e; y = 26; break;  // 1/4
                  case 32: r = 0x3d; y  = 8; break;  // 1/2
                  case 64: r = 0x3c; y =  9; break;  // 1/1
                  default: r = 0x3f; y = 25; break;
                  }
            }
      pStatus.setFont(f, 0);
      fprintf(f, "%d %d moveto <%02x> show\n", pt.x(), y, r);
      }

//---------------------------------------------------------
//   print
//---------------------------------------------------------

void BracketItem::print(FILE* f) const
      {
      fprintf(f, "\n%%Bracket\n");
      fprintf(f, "gsave [ 1 0 0 1 -8 %d] concat\n", 42); // _h+18);
      fprintf(f, "-0.938 0.00 moveto\n");
      fprintf(f, "14.74 14.15 -4.61 21.44 6.35 42.13 curveto\n");
      fprintf(f, "-7.91 20.1 11.12 9.65 -0.94 0.00 curveto\n");
      fprintf(f, " fill\n");
      fprintf(f, "-0.938 0.00 moveto\n");
      fprintf(f, "14.74 -14.15 -4.61 -21.44 6.35 -42.13 curveto\n");
      fprintf(f, "-7.91 -20.1 11.12 -9.65 -0.94 0.00 curveto\n");
      fprintf(f, " fill\n");
      fprintf(f, "grestore\n");
      }

//---------------------------------------------------------
//   Symbol::print
//---------------------------------------------------------

void Symbol::print(FILE* f) const
      {
      int code;
      switch(_event->dataA()) {
            case 0:     code = G_ppp; break;
            case 1:     code = G_pp;  break;
            case 2:     code = G_p; break;
            case 3:     code = G_f; break;
            case 4:     code = G_ff; break;
            case 5:     code = G_fff; break;
            case 6:     code = G_mp; break;
            case 7:     code = G_mf; break;
            case 8:     code = G_sfz; break;
            case 9:     code = G_sf; break;
            case 10:    code = G_sff; break;
            case 11:    code = G_fp; break;
            default:
                  printf("Symbol:print(%d): not implem.\n", _event->dataA());
                  return;
            }
      printGlyphC(f, code, double(pt.x()), double(pt.y()));
      }

//---------------------------------------------------------
//   TimesigItem::print
//---------------------------------------------------------

void TimesigItem::print(FILE* f) const
      {
      double x = double(pt.x());
      fprintf(f, "%%Timesig\n");
      switch(type) {
            case 0:
                  pStatus.setFont(f, 0);
                  fprintf(f, "%.2f %.2f moveto <53> show\n", x, 12.0); break;
                  break;
            case 1:
                  pStatus.setFont(f, 0);
                  fprintf(f, "%.2f %.2f moveto <52> show\n", x, 12.0); break;
                  break;
            case 2:
            default:
                  pStatus.setFont(f, &font);
                  {
                  QString nomTxt;
                  QString denTxt;
                  denTxt.setNum(denom);
                  nomTxt.setNum(nom1+nom2+nom3);
                  fprintf(f, "%.2f %.2f moveto (%s) dup stringwidth pop 0 exch 2 div sub 0 rmoveto show\n",
                     x, 11.0, nomTxt.ascii());
                  fprintf(f, "%.2f %.2f moveto (%s) dup stringwidth pop 0 exch 2 div sub 0 rmoveto show\n",
                     x, 24.0, denTxt.ascii());
                  }
            }
      }


//---------------------------------------------------------
//   Beam::print
//---------------------------------------------------------

extern int len2beams(int len);

void Beam::print(FILE* f) const
      {
      int minbeams = 5;
      int maxbeams = 0;
      for (ciNote i = notes.begin(); i != notes.end(); ++i) {
            int b = len2beams((*i)->len());
            if (b < minbeams)
                  minbeams = b;
            if (b > maxbeams)
                  maxbeams = b;
            }
      int bh = 20 + (maxbeams-1)*(3+2);

      NoteItem* firstItem = notes.front();
      NoteItem* lastItem = notes.back();

      double x1 = lastItem->pos().x();
      double x2 = firstItem->pos().x();

      bool up = firstItem->hline() > lastItem->hline();

      enum BeamPos { ABOVE, BELOW };
      BeamPos beamPos = ((min+max)/2) <= 4 ? BELOW : ABOVE;

      int minX = min * 3;
      int maxX = max * 3;
      double y1;
      double y2;
      double sw  = 3.75;
      double lw2 = stemLineWidth/2.0;

      if (beamPos == BELOW) {
            x1 += +lw2-sw;
            x2 += -lw2-sw;
            y1 = bh;
            y2 = bh;
            }
      else {
            x1 += sw+lw2;
            x2 += sw-lw2;
            y1 = -bh;
            y2 = -bh;
            }
      if (up) {
            y1 += maxX;
            y2 += minX;
            }
      else {
            y1 += minX;
            y2 += maxX;
            }

      double dx1 = x2-x1;
      double dy1 = y2-y1;

      pStatus.setLineWidth(f, stemLineWidth);
      for (ciNote i = notes.begin(); i != notes.end(); ++i) {
            double x3 = (*i)->pos().x();
            double y3 = (*i)->hline()*3;
            if (beamPos == BELOW)
                  x3 -= 3.75;
            else
                  x3 += 3.75;
            double y4 = y1 + ((x2-x3)*(dy1)+dx1/2)/dx1;
            fprintf(f, "%f %f moveto %f %f lineto stroke\n", x3, y3, x3, y4);
            int b = len2beams((*i)->len());
            //
            // draw partial beams
            //
            if (minbeams < b)
                  fprintf(stderr, "sorry, not partial beams implemented\n");
            }

      pStatus.setLineWidth(f, 0);
      double b2 = beamWidth/2.0;
      for (int i = 0; i < minbeams; ++i) {
            double hoff = (beamPos == BELOW) ? -(i*5) : (i*5);
            fprintf(f, "%f %f moveto\n",      x2, y1+hoff - b2);
            fprintf(f, "%f %f lineto\n",      x2, y1+hoff + b2);
            fprintf(f, "%f %f lineto\n",      x1, y2+hoff + b2);
            fprintf(f, "%f %f lineto\n",      x1, y2+hoff - b2);
            fprintf(f, "%f %f lineto fill\n", x2, y1+hoff - b2);
            }
      }


