home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 110 / EnigmaAmiga110CD.iso / indispensabili / utility / apdf / xpdf-0.80 / xpdf / textoutputdev.cc < prev    next >
C/C++ Source or Header  |  1999-06-23  |  17KB  |  635 lines

  1. //========================================================================
  2. //
  3. // TextOutputDev.cc
  4. //
  5. // Copyright 1997 Derek B. Noonburg
  6. //
  7. //========================================================================
  8.  
  9. #ifdef __GNUC__
  10. #pragma implementation
  11. #endif
  12.  
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <stddef.h>
  16. #include <ctype.h>
  17. #include "GString.h"
  18. #include "gmem.h"
  19. #include "config.h"
  20. #include "Error.h"
  21. #include "GfxState.h"
  22. #include "GfxFont.h"
  23. #include "TextOutputDev.h"
  24.  
  25. #include "TextOutputFontInfo.h"
  26.  
  27. //------------------------------------------------------------------------
  28. // Character substitutions
  29. //------------------------------------------------------------------------
  30.  
  31. static char *isoLatin1Subst[] = {
  32.   "L",                          // Lslash
  33.   "OE",                         // OE
  34.   "S",                          // Scaron
  35.   "Y",                          // Ydieresis
  36.   "Z",                          // Zcaron
  37.   "fi",                         // fi
  38.   "fl",                         // fl
  39.   "i",                          // dotlessi
  40.   "l",                          // lslash
  41.   "oe",                         // oe
  42.   "s",                          // scaron
  43.   "z",                          // zcaron
  44.   "*",                          // bullet
  45.   "...",                        // ellipsis
  46.   "-", "-",                     // emdash, hyphen
  47.   "\"", "\"",                   // quotedblleft, quotedblright
  48.   "'",                          // quotesingle
  49.   "TM"                          // trademark
  50. };
  51.  
  52. static char *ascii7Subst[] = {
  53.   "A", "A", "A", "A",           // A{acute,circumflex,dieresis,grave}
  54.   "A", "A",                     // A{ring,tilde}
  55.   "AE",                         // AE
  56.   "C",                          // Ccedilla
  57.   "E", "E", "E", "E",           // E{acute,circumflex,dieresis,grave}
  58.   "I", "I", "I", "I",           // I{acute,circumflex,dieresis,grave}
  59.   "L",                          // Lslash
  60.   "N",                          // Ntilde
  61.   "O", "O", "O", "O",           // O{acute,circumflex,dieresis,grave}
  62.   "O", "O",                     // O{slash,tilde}
  63.   "OE",                         // OE
  64.   "S",                          // Scaron
  65.   "U", "U", "U", "U",           // U{acute,circumflex,dieresis,grave}
  66.   "Y", "Y",                     // T{acute,dieresis}
  67.   "Z",                          // Zcaron
  68.   "a", "a", "a", "a",           // a{acute,circumflex,dieresis,grave}
  69.   "a", "a",                     // a{ring,tilde}
  70.   "ae",                         // ae
  71.   "c",                          // ccedilla
  72.   "e", "e", "e", "e",           // e{acute,circumflex,dieresis,grave}
  73.   "fi",                         // fi
  74.   "fl",                         // fl
  75.   "i",                          // dotlessi
  76.   "i", "i", "i", "i",           // i{acute,circumflex,dieresis,grave}
  77.   "l",                          // lslash
  78.   "n",                          // ntilde
  79.   "o", "o", "o", "o",           // o{acute,circumflex,dieresis,grave}
  80.   "o", "o",                     // o{slash,tilde}
  81.   "oe",                         // oe
  82.   "s",                          // scaron
  83.   "u", "u", "u", "u",           // u{acute,circumflex,dieresis,grave}
  84.   "y", "y",                     // t{acute,dieresis}
  85.   "z",                          // zcaron
  86.   "|",                          // brokenbar
  87.   "*",                          // bullet
  88.   "...",                        // ellipsis
  89.   "-", "-", "-",                // emdash, endash, hyphen
  90.   "\"", "\"",                   // quotedblleft, quotedblright
  91.   "'",                          // quotesingle
  92.   "(R)",                        // registered
  93.   "TM"                          // trademark
  94. };
  95.  
  96. //------------------------------------------------------------------------
  97. // TextString
  98. //------------------------------------------------------------------------
  99.  
  100. TextString::TextString(GfxState *state, GBool hexCodes1) {
  101.   double x, y, h;
  102.  
  103.   state->transform(state->getCurX(), state->getCurY(), &x, &y);
  104.   h = state->getTransformedFontSize();
  105.   //~ yMin/yMax computation should use font ascent/descent values
  106.   yMin = y - 0.95 * h;
  107.   yMax = yMin + 1.3 * h;
  108.   col = 0;
  109.   text = new GString();
  110.   xRight = NULL;
  111.   yxNext = NULL;
  112.   xyNext = NULL;
  113.   hexCodes = hexCodes1;
  114. }
  115.  
  116. TextString::~TextString() {
  117.   delete text;
  118.   gfree(xRight);
  119. }
  120.  
  121. void TextString::addChar(GfxState *state, double x, double y,
  122.              double dx, double dy,
  123.              Guchar c, GBool useASCII7) {
  124.   char *charName, *sub;
  125.   int c1;
  126.   int i, j, n, m;
  127.  
  128.   // get current index
  129.   i = text->getLength();
  130.  
  131.   // append translated character(s) to string
  132.   sub = NULL;
  133.   n = 1;
  134.   /*if(c>128) {
  135.       printf("addchar(%d,'%c')\n",c,c);
  136.       printf("font=%s\n",state->getFont()->getTag()->getCString());
  137.   }*/
  138.   if ((charName = state->getFont()->getCharName(c))) {
  139.     if (useASCII7)
  140.       c1 = ascii7Encoding.getCharCode(charName);
  141.     else
  142.       c1 = isoLatin1Encoding.getCharCode(charName);
  143.     /*if(c>128)
  144.     printf(" -> %s -> %d,'%c'\n",charName,c1,c1);*/
  145.     if (c1 < 0) {
  146.       m = strlen(charName);
  147.       if (hexCodes && m == 3 &&
  148.       (charName[0] == 'B' || charName[0] == 'C' ||
  149.        charName[0] == 'G') &&
  150.       isxdigit(charName[1]) && isxdigit(charName[2])) {
  151.     sscanf(charName+1, "%x", &c1);
  152.       } else if (!hexCodes && m >= 2 && m <= 3 &&
  153.          isdigit(charName[0]) && isdigit(charName[1])) {
  154.     c1 = atoi(charName);
  155.     if (c1 >= 256)
  156.       c1 = -1;
  157.       } else if (!hexCodes && m >= 3 && m <= 5 && isdigit(charName[1])) {
  158.     c1 = atoi(charName+1);
  159.     if (c1 >= 256)
  160.       c1 = -1;
  161.       }
  162.       //~ this is a kludge -- is there a standard internal encoding
  163.       //~ used by all/most Type 1 fonts?
  164.       if (c1 == 262)            // hyphen
  165.     c1 = 45;
  166.       else if (c1 == 266)       // emdash
  167.     c1 = 208;
  168.       if (useASCII7)
  169.     c1 = ascii7Encoding.getCharCode(isoLatin1Encoding.getCharName(c1));
  170.     }
  171.     if (useASCII7) {
  172.       if (c1 >= 128) {
  173.     sub = ascii7Subst[c1 - 128];
  174.     n = strlen(sub);
  175.       }
  176.     } else {
  177.       if (c1 >= 256) {
  178.     sub = isoLatin1Subst[c1 - 256];
  179.     n = strlen(sub);
  180.       }
  181.     }
  182.   } else {
  183.     c1 = -1;
  184.   }
  185.   if (sub)
  186.     text->append(sub);
  187.   else if (c1 >= 0)
  188.     text->append((char)c1);
  189.   else
  190.     text->append(' ');
  191.  
  192.   // update position information
  193.   if (i+n > ((i+15) & ~15))
  194.     xRight = (double *)grealloc(xRight, ((i+n+15) & ~15) * sizeof(double));
  195.   if (i == 0)
  196.     xMin = x;
  197.   for (j = 0; j < n; ++j)
  198.     xRight[i+j] = x + ((j+1) * dx) / n;
  199.   xMax = x + dx;
  200. }
  201.  
  202. //------------------------------------------------------------------------
  203. // TextPage
  204. //------------------------------------------------------------------------
  205.  
  206. TextPage::TextPage(GBool useASCII71) {
  207.   useASCII7 = useASCII71;
  208.   curStr = NULL;
  209.   yxStrings = NULL;
  210.   xyStrings = NULL;
  211. }
  212.  
  213. TextPage::~TextPage() {
  214.   clear();
  215. }
  216.  
  217. void TextPage::beginString(GfxState *state, GString *s, GBool hexCodes) {
  218.   curStr = new TextString(state, hexCodes);
  219. }
  220.  
  221. void TextPage::addChar(GfxState *state, double x, double y,
  222.                double dx, double dy, Guchar c) {
  223.   double x1, y1, w1, h1;
  224.  
  225.   state->transform(x, y, &x1, &y1);
  226.   state->transformDelta(dx, dy, &w1, &h1);
  227.   curStr->addChar(state, x1, y1, w1, h1, c, useASCII7);
  228. }
  229.  
  230. void TextPage::endString() {
  231.   TextString *p1, *p2;
  232.   double h, y1, y2;
  233.  
  234.   // throw away zero-length strings -- they don't have valid xMin/xMax
  235.   // values, and they're useless anyway
  236.   if (curStr->text->getLength() == 0) {
  237.     delete curStr;
  238.     curStr = NULL;
  239.     return;
  240.   }
  241.  
  242. #if 0 //~tmp
  243.   if (curStr->yMax - curStr->yMin > 20) {
  244.     delete curStr;
  245.     curStr = NULL;
  246.     return;
  247.   }
  248. #endif
  249.  
  250.   // insert string in y-major list
  251.   h = curStr->yMax - curStr->yMin;
  252.   y1 = curStr->yMin + 0.5 * h;
  253.   y2 = curStr->yMin + 0.8 * h;
  254.   for (p1 = NULL, p2 = yxStrings; p2; p1 = p2, p2 = p2->yxNext) {
  255.     if (y1 < p2->yMin || (y2 < p2->yMax && curStr->xMax < p2->xMin))
  256.       break;
  257.   }
  258.   if (p1)
  259.     p1->yxNext = curStr;
  260.   else
  261.     yxStrings = curStr;
  262.   curStr->yxNext = p2;
  263.   curStr = NULL;
  264. }
  265.  
  266. void TextPage::coalesce() {
  267.   TextString *str1, *str2;
  268.   double space, d;
  269.   int n, i;
  270.  
  271. #if 0 //~ for debugging
  272.   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
  273.     printf("x=%3d..%3d  y=%3d..%3d  size=%2d '%s'\n",
  274.        (int)str1->xMin, (int)str1->xMax, (int)str1->yMin, (int)str1->yMax,
  275.        (int)(str1->yMax - str1->yMin), str1->text->getCString());
  276.   }
  277.   printf("\n------------------------------------------------------------\n\n");
  278. #endif
  279.   str1 = yxStrings;
  280.   while (str1 && (str2 = str1->yxNext)) {
  281.     space = str1->yMax - str1->yMin;
  282.     d = str2->xMin - str1->xMax;
  283. #if 0 //~tmp
  284.     if (str2->yMin < str1->yMax && d > -0.1 * space && d < 0.2 * space) {
  285. #else
  286.     if (str2->yMin < str1->yMax && d > -0.5 * space && d < space) {
  287. #endif
  288.       n = str1->text->getLength();
  289.       if (d > 0.1 * space)
  290.     str1->text->append(' ');
  291.       str1->text->append(str2->text);
  292.       str1->xRight = (double *)
  293.     grealloc(str1->xRight, str1->text->getLength() * sizeof(double));
  294.       if (d > 0.1 * space)
  295.     str1->xRight[n++] = str2->xMin;
  296.       for (i = 0; i < str2->text->getLength(); ++i)
  297.     str1->xRight[n++] = str2->xRight[i];
  298.       if (str2->xMax > str1->xMax)
  299.     str1->xMax = str2->xMax;
  300.       if (str2->yMax > str1->yMax)
  301.     str1->yMax = str2->yMax;
  302.       str1->yxNext = str2->yxNext;
  303.       delete str2;
  304.     } else {
  305.       str1 = str2;
  306.     }
  307.   }
  308. }
  309.  
  310. GBool TextPage::findText(char *s, GBool top, GBool bottom,
  311.              double *xMin, double *yMin,
  312.              double *xMax, double *yMax) {
  313.   TextString *str;
  314.   char *p, *p1, *q;
  315.   int n, m, i;
  316.   double x;
  317.  
  318.   // scan all strings on page
  319.   n = strlen(s);
  320.   for (str = yxStrings; str; str = str->yxNext) {
  321.  
  322.     // check: above top limit?
  323.     if (!top && (str->yMax < *yMin ||
  324.          (str->yMin < *yMin && str->xMax <= *xMin)))
  325.       continue;
  326.  
  327.     // check: below bottom limit?
  328.     if (!bottom && (str->yMin > *yMax ||
  329.             (str->yMax > *yMax && str->xMin >= *xMax)))
  330.       return gFalse;
  331.  
  332.     // search each position in this string
  333.     m = str->text->getLength();
  334.     for (i = 0, p = str->text->getCString(); i <= m - n; ++i, ++p) {
  335.  
  336.       // check: above top limit?
  337.       if (!top && str->yMin < *yMin) {
  338.     x = (((i == 0) ? str->xMin : str->xRight[i-1]) + str->xRight[i]) / 2;
  339.     if (x < *xMin)
  340.       continue;
  341.       }
  342.  
  343.       // check: below bottom limit?
  344.       if (!bottom && str->yMax > *yMax) {
  345.     x = (((i == 0) ? str->xMin : str->xRight[i-1]) + str->xRight[i]) / 2;
  346.     if (x > *xMax)
  347.       return gFalse;
  348.       }
  349.  
  350.       // compare the strings
  351.       for (p1 = p, q = s; *q; ++p1, ++q) {
  352.     if (tolower(*p1) != tolower(*q))
  353.       break;
  354.       }
  355.  
  356.       // found it
  357.       if (!*q) {
  358.     *xMin = (i == 0) ? str->xMin : str->xRight[i-1];
  359.     *xMax = str->xRight[i+n-1];
  360.     *yMin = str->yMin;
  361.     *yMax = str->yMax;
  362.     return gTrue;
  363.       }
  364.     }
  365.   }
  366.   return gFalse;
  367. }
  368.  
  369. GString *TextPage::getText(double xMin, double yMin,
  370.                double xMax, double yMax) {
  371.   GString *s;
  372.   TextString *str1;
  373.   double x0, x1, x2, y;
  374.   double xPrev, yPrev;
  375.   int i1, i2;
  376.   GBool multiLine;
  377.  
  378.   s = new GString();
  379.   xPrev = yPrev = 0;
  380.   multiLine = gFalse;
  381.   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
  382.     y = 0.5 * (str1->yMin + str1->yMax);
  383.     if (y > yMax)
  384.       break;
  385.     if (y > yMin && str1->xMin < xMax && str1->xMax > xMin) {
  386.       x0 = x1 = x2 = str1->xMin;
  387.       for (i1 = 0; i1 < str1->text->getLength(); ++i1) {
  388.     x0 = (i1==0) ? str1->xMin : str1->xRight[i1-1];
  389.     x1 = str1->xRight[i1];
  390.     if (0.5 * (x0 + x1) >= xMin)
  391.       break;
  392.       }
  393.       for (i2 = str1->text->getLength() - 1; i2 > i1; --i2) {
  394.     x1 = (i2==0) ? str1->xMin : str1->xRight[i2-1];
  395.     x2 = str1->xRight[i2];
  396.     if (0.5 * (x1 + x2) <= xMax)
  397.       break;
  398.       }
  399.       if (s->getLength() > 0) {
  400.     if (x0 < xPrev || str1->yMin > yPrev) {
  401.       s->append('\n');
  402.       multiLine = gTrue;
  403.     } else {
  404.       s->append("    ");
  405.     }
  406.       }
  407.       s->append(str1->text->getCString() + i1, i2 - i1 + 1);
  408.       xPrev = x2;
  409.       yPrev = str1->yMax;
  410.     }
  411.   }
  412.   if (multiLine)
  413.     s->append('\n');
  414.   return s;
  415. }
  416.  
  417. void TextPage::dump(FILE *f) {
  418.   TextString *str1, *str2, *str3;
  419.   double yMin, yMax;
  420.   int col1, col2;
  421.   double d;
  422.  
  423.   // build x-major list
  424.   xyStrings = NULL;
  425.   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
  426.     for (str2 = NULL, str3 = xyStrings;
  427.      str3;
  428.      str2 = str3, str3 = str3->xyNext) {
  429.       if (str1->xMin < str3->xMin ||
  430.       (str1->xMin == str3->xMin && str1->yMin < str3->yMin))
  431.     break;
  432.     }
  433.     if (str2)
  434.       str2->xyNext = str1;
  435.     else
  436.       xyStrings = str1;
  437.     str1->xyNext = str3;
  438.   }
  439.  
  440.   // do column assignment
  441.   for (str1 = xyStrings; str1; str1 = str1->xyNext) {
  442.     col1 = 0;
  443.     for (str2 = xyStrings; str2 != str1; str2 = str2->xyNext) {
  444.       if (str1->xMin >= str2->xMax) {
  445.     col2 = str2->col + str2->text->getLength() + 4;
  446.     if (col2 > col1)
  447.       col1 = col2;
  448.       } else if (str1->xMin > str2->xMin) {
  449.     col2 = str2->col +
  450.            (int)(((str1->xMin - str2->xMin) / (str2->xMax - str2->xMin)) *
  451.              str2->text->getLength());
  452.     if (col2 > col1) {
  453.       col1 = col2;
  454.     }
  455.       }
  456.     }
  457.     str1->col = col1;
  458.   }
  459.  
  460. #if 0 //~ for debugging
  461.   fprintf(f, "~~~~~~~~~~\n");
  462.   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
  463.     fprintf(f, "(%4d,%4d) - (%4d,%4d) [%3d] %s\n",
  464.         (int)str1->xMin, (int)str1->yMin, (int)str1->xMax, (int)str1->yMax,
  465.         str1->col, str1->text->getCString());
  466.   }
  467.   fprintf(f, "~~~~~~~~~~\n");
  468. #endif
  469.  
  470.   // output
  471.   col1 = 0;
  472.   yMax = yxStrings ? yxStrings->yMax : 0;
  473.   for (str1 = yxStrings; str1; str1 = str1->yxNext) {
  474.  
  475.     // line this string up with the correct column
  476.     for (; col1 < str1->col; ++col1)
  477.       fputc(' ', f);
  478.  
  479.     // print the string
  480.     fputs(str1->text->getCString(), f);
  481.  
  482.     // increment column
  483.     col1 += str1->text->getLength();
  484.  
  485.     // update yMax for this line
  486.     if (str1->yMax > yMax)
  487.       yMax = str1->yMax;
  488.  
  489.     // if we've hit the end of the line...
  490. #if 0 //~
  491.     if (!(str1->yxNext && str1->yxNext->yMin < str1->yMax &&
  492.       str1->yxNext->xMin >= str1->xMax)) {
  493. #else
  494.     if (!(str1->yxNext &&
  495.       str1->yxNext->yMin < 0.2*str1->yMin + 0.8*str1->yMax &&
  496.       str1->yxNext->xMin >= str1->xMax)) {
  497. #endif
  498.  
  499.       // print a return
  500.       fputc('\n', f);
  501.  
  502.       // print extra vertical space if necessary
  503.       if (str1->yxNext) {
  504.  
  505.     // find yMin for next line
  506.     yMin = str1->yxNext->yMin;
  507.     for (str2 = str1->yxNext; str2; str2 = str2->yxNext) {
  508.       if (str2->yMin < yMin)
  509.         yMin = str2->yMin;
  510.       if (!(str2->yxNext && str2->yxNext->yMin < str2->yMax &&
  511.         str2->yxNext->xMin >= str2->xMax))
  512.         break;
  513.     }
  514.       
  515.     // print the space
  516.     d = (int)((yMin - yMax) / (str1->yMax - str1->yMin) + 0.5);
  517.     for (; d > 0; --d)
  518.       fputc('\n', f);
  519.       }
  520.  
  521.       // set up for next line
  522.       col1 = 0;
  523.       yMax = str1->yxNext ? str1->yxNext->yMax : 0;
  524.     }
  525.   }
  526. }
  527.  
  528. void TextPage::clear() {
  529.   TextString *p1, *p2;
  530.  
  531.   if (curStr) {
  532.     delete curStr;
  533.     curStr = NULL;
  534.   }
  535.   for (p1 = yxStrings; p1; p1 = p2) {
  536.     p2 = p1->yxNext;
  537.     delete p1;
  538.   }
  539.   yxStrings = NULL;
  540.   xyStrings = NULL;
  541. }
  542.  
  543. //------------------------------------------------------------------------
  544. // TextOutputDev
  545. //------------------------------------------------------------------------
  546.  
  547. TextOutputDev::TextOutputDev(char *fileName, GBool useASCII7) {
  548.   text = NULL;
  549.   ok = gTrue;
  550.  
  551.   // open file
  552.   needClose = gFalse;
  553.   if (fileName) {
  554.     if (!strcmp(fileName, "-")) {
  555.       f = stdout;
  556.     } else if ((f = fopen(fileName, "w"))) {
  557.       needClose = gTrue;
  558.     } else {
  559.       error(-1, "Couldn't open text file '%s'", fileName);
  560.       ok = gFalse;
  561.       return;
  562.     }
  563.   } else {
  564.     f = NULL;
  565.   }
  566.  
  567.   // set up text object
  568.   text = new TextPage(useASCII7);
  569. }
  570.  
  571. TextOutputDev::~TextOutputDev() {
  572.   if (needClose)
  573.     fclose(f);
  574.   if (text)
  575.     delete text;
  576. }
  577.  
  578. void TextOutputDev::startPage(int pageNum, GfxState *state) {
  579.   text->clear();
  580. }
  581.  
  582. void TextOutputDev::endPage() {
  583.   text->coalesce();
  584.   if (f) {
  585.     text->dump(f);
  586.     fputc('\n', f);
  587.     fputs("\f\n", f);
  588.     fputc('\n', f);
  589.   }
  590. }
  591.  
  592. void TextOutputDev::updateFont(GfxState *state) {
  593.   GfxFont *font;
  594.   char *charName;
  595.   int c;
  596.  
  597.   // look for hex char codes in subsetted font
  598.   hexCodes = gFalse;
  599.   if ((font = state->getFont())) {
  600.     for (c = 0; c < 256; ++c) {
  601.       if ((charName = font->getCharName(c))) {
  602.     if ((charName[0] == 'B' || charName[0] == 'C' ||
  603.          charName[0] == 'G') &&
  604.         strlen(charName) == 3 &&
  605.         ((charName[1] >= 'a' && charName[1] <= 'f') ||
  606.          (charName[1] >= 'A' && charName[1] <= 'F') ||
  607.          (charName[2] >= 'a' && charName[2] <= 'f') ||
  608.          (charName[2] >= 'A' && charName[2] <= 'F'))) {
  609.       hexCodes = gTrue;
  610.       break;
  611.     }
  612.       }
  613.     }
  614.   }
  615. }
  616.  
  617. void TextOutputDev::beginString(GfxState *state, GString *s) {
  618.   text->beginString(state, s, hexCodes);
  619. }
  620.  
  621. void TextOutputDev::endString(GfxState *state) {
  622.   text->endString();
  623. }
  624.  
  625. void TextOutputDev::drawChar(GfxState *state, double x, double y,
  626.                  double dx, double dy, Guchar c) {
  627.   text->addChar(state, x, y, dx, dy, c);
  628. }
  629.  
  630. GBool TextOutputDev::findText(char *s, GBool top, GBool bottom,
  631.                   double *xMin, double *yMin,
  632.                   double *xMax, double *yMax) {
  633.   return text->findText(s, top, bottom, xMin, yMin, xMax, yMax);
  634. }
  635.