Diffstat (limited to 'noncore/unsupported/qpdf/xpdf/Gfx.cc') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/unsupported/qpdf/xpdf/Gfx.cc | 589 |
1 files changed, 444 insertions, 145 deletions
diff --git a/noncore/unsupported/qpdf/xpdf/Gfx.cc b/noncore/unsupported/qpdf/xpdf/Gfx.cc index c19971c..17d613e 100644 --- a/noncore/unsupported/qpdf/xpdf/Gfx.cc +++ b/noncore/unsupported/qpdf/xpdf/Gfx.cc | |||
@@ -3,5 +3,5 @@ | |||
3 | // Gfx.cc | 3 | // Gfx.cc |
4 | // | 4 | // |
5 | // Copyright 1996 Derek B. Noonburg | 5 | // Copyright 1996-2002 Glyph & Cog, LLC |
6 | // | 6 | // |
7 | //======================================================================== | 7 | //======================================================================== |
@@ -31,4 +31,9 @@ | |||
31 | #include "Gfx.h" | 31 | #include "Gfx.h" |
32 | 32 | ||
33 | // the MSVC math.h doesn't define this | ||
34 | #ifndef M_PI | ||
35 | #define M_PI 3.14159265358979323846 | ||
36 | #endif | ||
37 | |||
33 | //------------------------------------------------------------------------ | 38 | //------------------------------------------------------------------------ |
34 | // constants | 39 | // constants |
@@ -41,4 +46,10 @@ | |||
41 | #define axialColorDelta (1 / 256.0) | 46 | #define axialColorDelta (1 / 256.0) |
42 | 47 | ||
48 | // Max number of splits along the t axis for a radial shading fill. | ||
49 | #define radialMaxSplits 256 | ||
50 | |||
51 | // Max delta allowed in any color component for a radial shading fill. | ||
52 | #define radialColorDelta (1 / 256.0) | ||
53 | |||
43 | //------------------------------------------------------------------------ | 54 | //------------------------------------------------------------------------ |
44 | // Operator table | 55 | // Operator table |
@@ -375,4 +386,5 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, fouble dpi, | |||
375 | 386 | ||
376 | xref = xrefA; | 387 | xref = xrefA; |
388 | subPage = gFalse; | ||
377 | printCommands = printCommandsA; | 389 | printCommands = printCommandsA; |
378 | 390 | ||
@@ -406,19 +418,52 @@ Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict, fouble dpi, | |||
406 | } | 418 | } |
407 | 419 | ||
408 | Gfx::~Gfx() { | 420 | Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict, |
409 | GfxResources *resPtr; | 421 | PDFRectangle *box, GBool crop, PDFRectangle *cropBox) { |
422 | int i; | ||
410 | 423 | ||
424 | xref = xrefA; | ||
425 | subPage = gTrue; | ||
426 | printCommands = gFalse; | ||
427 | |||
428 | // start the resource stack | ||
429 | res = new GfxResources(xref, resDict, NULL); | ||
430 | |||
431 | // initialize | ||
432 | out = outA; | ||
433 | state = new GfxState(72, box, 0, gFalse); | ||
434 | fontChanged = gFalse; | ||
435 | clip = clipNone; | ||
436 | ignoreUndef = 0; | ||
437 | for (i = 0; i < 6; ++i) { | ||
438 | baseMatrix[i] = state->getCTM()[i]; | ||
439 | } | ||
440 | |||
441 | // set crop box | ||
442 | if (crop) { | ||
443 | state->moveTo(cropBox->x1, cropBox->y1); | ||
444 | state->lineTo(cropBox->x2, cropBox->y1); | ||
445 | state->lineTo(cropBox->x2, cropBox->y2); | ||
446 | state->lineTo(cropBox->x1, cropBox->y2); | ||
447 | state->closePath(); | ||
448 | state->clip(); | ||
449 | out->clip(state); | ||
450 | state->clearPath(); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | Gfx::~Gfx() { | ||
411 | while (state->hasSaves()) { | 455 | while (state->hasSaves()) { |
412 | state = state->restore(); | 456 | state = state->restore(); |
413 | out->restoreState(state); | 457 | out->restoreState(state); |
414 | } | 458 | } |
415 | out->endPage(); | 459 | if (!subPage) { |
460 | out->endPage(); | ||
461 | } | ||
416 | while (res) { | 462 | while (res) { |
417 | resPtr = res->getNext(); | 463 | popResources(); |
418 | delete res; | ||
419 | res = resPtr; | ||
420 | } | 464 | } |
421 | if (state) | 465 | if (state) { |
422 | delete state; | 466 | delete state; |
467 | } | ||
423 | } | 468 | } |
424 | 469 | ||
@@ -450,9 +495,9 @@ void Gfx::go(GBool topLevel) { | |||
450 | Object obj; | 495 | Object obj; |
451 | Object args[maxArgs]; | 496 | Object args[maxArgs]; |
452 | int numCmds, numArgs; | 497 | int numArgs; |
453 | int i; | 498 | int i; |
454 | 499 | ||
455 | // scan a sequence of objects | 500 | // scan a sequence of objects |
456 | numCmds = 0; | 501 | updateLevel = 0; |
457 | numArgs = 0; | 502 | numArgs = 0; |
458 | parser->getObj(&obj); | 503 | parser->getObj(&obj); |
@@ -477,7 +522,7 @@ void Gfx::go(GBool topLevel) { | |||
477 | 522 | ||
478 | // periodically update display | 523 | // periodically update display |
479 | if (++numCmds == 200) { | 524 | if (++updateLevel >= 20000) { |
480 | out->dump(); | 525 | out->dump(); |
481 | numCmds = 0; | 526 | updateLevel = 0; |
482 | } | 527 | } |
483 | 528 | ||
@@ -520,5 +565,5 @@ void Gfx::go(GBool topLevel) { | |||
520 | 565 | ||
521 | // update display | 566 | // update display |
522 | if (topLevel && numCmds > 0) { | 567 | if (topLevel && updateLevel > 0) { |
523 | out->dump(); | 568 | out->dump(); |
524 | } | 569 | } |
@@ -1139,5 +1184,5 @@ void Gfx::doPatternFill(GBool eoFill) { | |||
1139 | int xi0, yi0, xi1, yi1, xi, yi; | 1184 | int xi0, yi0, xi1, yi1, xi, yi; |
1140 | fouble *ctm, *btm, *ptm; | 1185 | fouble *ctm, *btm, *ptm; |
1141 | fouble m[6], ictm[6], m1[6], im[6], imb[6]; | 1186 | fouble m[6], ictm[6], m1[6], imb[6]; |
1142 | fouble det; | 1187 | fouble det; |
1143 | fouble xstep, ystep; | 1188 | fouble xstep, ystep; |
@@ -1190,13 +1235,4 @@ void Gfx::doPatternFill(GBool eoFill) { | |||
1190 | m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; | 1235 | m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; |
1191 | 1236 | ||
1192 | // construct a (current space) -> (pattern space) transform matrix | ||
1193 | det = 1 / (m[0] * m[3] - m[1] * m[2]); | ||
1194 | im[0] = m[3] * det; | ||
1195 | im[1] = -m[1] * det; | ||
1196 | im[2] = -m[2] * det; | ||
1197 | im[3] = m[0] * det; | ||
1198 | im[4] = (m[2] * m[5] - m[3] * m[4]) * det; | ||
1199 | im[5] = (m[1] * m[4] - m[0] * m[5]) * det; | ||
1200 | |||
1201 | // construct a (base space) -> (pattern space) transform matrix | 1237 | // construct a (base space) -> (pattern space) transform matrix |
1202 | det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]); | 1238 | det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]); |
@@ -1332,4 +1368,7 @@ void Gfx::opShFill(Object args[], int numArgs) { | |||
1332 | doAxialShFill((GfxAxialShading *)shading); | 1368 | doAxialShFill((GfxAxialShading *)shading); |
1333 | break; | 1369 | break; |
1370 | case 3: | ||
1371 | doRadialShFill((GfxRadialShading *)shading); | ||
1372 | break; | ||
1334 | } | 1373 | } |
1335 | 1374 | ||
@@ -1344,7 +1383,4 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { | |||
1344 | fouble xMin, yMin, xMax, yMax; | 1383 | fouble xMin, yMin, xMax, yMax; |
1345 | fouble x0, y0, x1, y1; | 1384 | fouble x0, y0, x1, y1; |
1346 | fouble det; | ||
1347 | fouble *ctm; | ||
1348 | fouble ictm[6]; | ||
1349 | fouble dx, dy, mul; | 1385 | fouble dx, dy, mul; |
1350 | fouble tMin, tMax, t, tx, ty; | 1386 | fouble tMin, tMax, t, tx, ty; |
@@ -1358,52 +1394,6 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { | |||
1358 | int i, j, k, kk; | 1394 | int i, j, k, kk; |
1359 | 1395 | ||
1360 | // get clip region bbox and transform to current user space | 1396 | // get the clip region bbox |
1361 | state->getClipBBox(&x0, &y0, &x1, &y1); | 1397 | state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
1362 | ctm = state->getCTM(); | ||
1363 | det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]); | ||
1364 | ictm[0] = ctm[3] * det; | ||
1365 | ictm[1] = -ctm[1] * det; | ||
1366 | ictm[2] = -ctm[2] * det; | ||
1367 | ictm[3] = ctm[0] * det; | ||
1368 | ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; | ||
1369 | ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; | ||
1370 | xMin = xMax = x0 * ictm[0] + y0 * ictm[2] + ictm[4]; | ||
1371 | yMin = yMax = x0 * ictm[1] + y0 * ictm[3] + ictm[5]; | ||
1372 | tx = x0 * ictm[0] + y1 * ictm[2] + ictm[4]; | ||
1373 | ty = x0 * ictm[1] + y1 * ictm[3] + ictm[5]; | ||
1374 | if (tx < xMin) { | ||
1375 | xMin = tx; | ||
1376 | } else if (tx > xMax) { | ||
1377 | xMax = tx; | ||
1378 | } | ||
1379 | if (ty < yMin) { | ||
1380 | yMin = ty; | ||
1381 | } else if (ty > yMax) { | ||
1382 | yMax = ty; | ||
1383 | } | ||
1384 | tx = x1 * ictm[0] + y0 * ictm[2] + ictm[4]; | ||
1385 | ty = x1 * ictm[1] + y0 * ictm[3] + ictm[5]; | ||
1386 | if (tx < xMin) { | ||
1387 | xMin = tx; | ||
1388 | } else if (tx > xMax) { | ||
1389 | xMax = tx; | ||
1390 | } | ||
1391 | if (ty < yMin) { | ||
1392 | yMin = ty; | ||
1393 | } else if (ty > yMax) { | ||
1394 | yMax = ty; | ||
1395 | } | ||
1396 | tx = x1 * ictm[0] + y1 * ictm[2] + ictm[4]; | ||
1397 | ty = x1 * ictm[1] + y1 * ictm[3] + ictm[5]; | ||
1398 | if (tx < xMin) { | ||
1399 | xMin = tx; | ||
1400 | } else if (tx > xMax) { | ||
1401 | xMax = tx; | ||
1402 | } | ||
1403 | if (ty < yMin) { | ||
1404 | yMin = ty; | ||
1405 | } else if (ty > yMax) { | ||
1406 | yMax = ty; | ||
1407 | } | ||
1408 | 1398 | ||
1409 | // compute min and max t values, based on the four corners of the | 1399 | // compute min and max t values, based on the four corners of the |
@@ -1620,4 +1610,200 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { | |||
1620 | } | 1610 | } |
1621 | 1611 | ||
1612 | void Gfx::doRadialShFill(GfxRadialShading *shading) { | ||
1613 | fouble sMin, sMax, xMin, yMin, xMax, yMax; | ||
1614 | fouble x0, y0, r0, x1, y1, r1, t0, t1; | ||
1615 | int nComps; | ||
1616 | GfxColor colorA, colorB; | ||
1617 | fouble xa, ya, xb, yb, ra, rb; | ||
1618 | fouble ta, tb, sa, sb; | ||
1619 | int ia, ib, k, n; | ||
1620 | fouble *ctm; | ||
1621 | fouble angle, t; | ||
1622 | |||
1623 | // get the shading info | ||
1624 | shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); | ||
1625 | t0 = shading->getDomain0(); | ||
1626 | t1 = shading->getDomain1(); | ||
1627 | nComps = shading->getColorSpace()->getNComps(); | ||
1628 | |||
1629 | // compute the (possibly extended) s range | ||
1630 | sMin = 0; | ||
1631 | sMax = 1; | ||
1632 | if (shading->getExtend0()) { | ||
1633 | if (r0 < r1) { | ||
1634 | // extend the smaller end | ||
1635 | sMin = -r0 / (r1 - r0); | ||
1636 | } else { | ||
1637 | // extend the larger end | ||
1638 | //~ this computes the diagonal of the bounding box -- we should | ||
1639 | //~ really compute the intersection of the moving/expanding | ||
1640 | //~ circles with each of the four corners and look for the max | ||
1641 | //~ radius | ||
1642 | state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); | ||
1643 | sMin = (sqrt((xMax - xMin) * (xMax - xMin) + | ||
1644 | (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); | ||
1645 | if (sMin > 0) { | ||
1646 | sMin = 0; | ||
1647 | } else if (sMin < -20) { | ||
1648 | // sanity check | ||
1649 | sMin = -20; | ||
1650 | } | ||
1651 | } | ||
1652 | } | ||
1653 | if (shading->getExtend1()) { | ||
1654 | if (r1 < r0) { | ||
1655 | // extend the smaller end | ||
1656 | sMax = -r0 / (r1 - r0); | ||
1657 | } else if (r1 > r0) { | ||
1658 | // extend the larger end | ||
1659 | state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); | ||
1660 | sMax = (sqrt((xMax - xMin) * (xMax - xMin) + | ||
1661 | (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); | ||
1662 | if (sMax < 1) { | ||
1663 | sMin = 1; | ||
1664 | } else if (sMax > 20) { | ||
1665 | // sanity check | ||
1666 | sMax = 20; | ||
1667 | } | ||
1668 | } | ||
1669 | } | ||
1670 | |||
1671 | // compute the number of steps into which circles must be divided to | ||
1672 | // achieve a curve flatness of 0.1 pixel in device space for the | ||
1673 | // largest circle (note that "device space" is 72 dpi when generating | ||
1674 | // PostScript, hence the relatively small 0.1 pixel accuracy) | ||
1675 | ctm = state->getCTM(); | ||
1676 | t = fabs(ctm[0]); | ||
1677 | if (fabs(ctm[1]) > t) { | ||
1678 | t = fabs(ctm[1]); | ||
1679 | } | ||
1680 | if (fabs(ctm[2]) > t) { | ||
1681 | t = fabs(ctm[2]); | ||
1682 | } | ||
1683 | if (fabs(ctm[3]) > t) { | ||
1684 | t = fabs(ctm[3]); | ||
1685 | } | ||
1686 | if (r0 > r1) { | ||
1687 | t *= r0; | ||
1688 | } else { | ||
1689 | t *= r1; | ||
1690 | } | ||
1691 | if (t < 1) { | ||
1692 | n = 3; | ||
1693 | } else { | ||
1694 | n = (int)(M_PI / acos(1 - 0.1 / t)); | ||
1695 | if (n < 3) { | ||
1696 | n = 3; | ||
1697 | } else if (n > 200) { | ||
1698 | n = 200; | ||
1699 | } | ||
1700 | } | ||
1701 | |||
1702 | // Traverse the t axis and do the shading. | ||
1703 | // | ||
1704 | // This generates and fills a series of rings. Each ring is defined | ||
1705 | // by two circles: | ||
1706 | // sa, ta, xa, ya, ra, colorA | ||
1707 | // sb, tb, xb, yb, rb, colorB | ||
1708 | // | ||
1709 | // The s/t axis is divided into radialMaxSplits parts; these parts | ||
1710 | // are combined as much as possible while respecting the | ||
1711 | // radialColorDelta parameter. | ||
1712 | |||
1713 | // setup for the start circle | ||
1714 | ia = 0; | ||
1715 | sa = sMin; | ||
1716 | ta = t0 + sa * (t1 - t0); | ||
1717 | xa = x0 + sa * (x1 - x0); | ||
1718 | ya = y0 + sa * (y1 - y0); | ||
1719 | ra = r0 + sa * (r1 - r0); | ||
1720 | if (ta < t0) { | ||
1721 | shading->getColor(t0, &colorA); | ||
1722 | } else if (ta > t1) { | ||
1723 | shading->getColor(t1, &colorA); | ||
1724 | } else { | ||
1725 | shading->getColor(ta, &colorA); | ||
1726 | } | ||
1727 | |||
1728 | while (ia < radialMaxSplits) { | ||
1729 | |||
1730 | // go as far along the t axis (toward t1) as we can, such that the | ||
1731 | // color difference is within the tolerance (radialColorDelta) -- | ||
1732 | // this uses bisection (between the current value, t, and t1), | ||
1733 | // limited to radialMaxSplits points along the t axis | ||
1734 | ib = radialMaxSplits; | ||
1735 | sb = sMin + ((fouble)ib / (fouble)radialMaxSplits) * (sMax - sMin); | ||
1736 | tb = t0 + sb * (t1 - t0); | ||
1737 | if (tb < t0) { | ||
1738 | shading->getColor(t0, &colorB); | ||
1739 | } else if (tb > t1) { | ||
1740 | shading->getColor(t1, &colorB); | ||
1741 | } else { | ||
1742 | shading->getColor(tb, &colorB); | ||
1743 | } | ||
1744 | while (ib - ia > 1) { | ||
1745 | for (k = 0; k < nComps; ++k) { | ||
1746 | if (fabs(colorB.c[k] - colorA.c[k]) > radialColorDelta) { | ||
1747 | break; | ||
1748 | } | ||
1749 | } | ||
1750 | if (k == nComps) { | ||
1751 | break; | ||
1752 | } | ||
1753 | ib = (ia + ib) / 2; | ||
1754 | sb = sMin + ((fouble)ib / (fouble)radialMaxSplits) * (sMax - sMin); | ||
1755 | tb = t0 + sb * (t1 - t0); | ||
1756 | if (tb < t0) { | ||
1757 | shading->getColor(t0, &colorB); | ||
1758 | } else if (tb > t1) { | ||
1759 | shading->getColor(t1, &colorB); | ||
1760 | } else { | ||
1761 | shading->getColor(tb, &colorB); | ||
1762 | } | ||
1763 | } | ||
1764 | |||
1765 | // compute center and radius of the circle | ||
1766 | xb = x0 + sb * (x1 - x0); | ||
1767 | yb = y0 + sb * (y1 - y0); | ||
1768 | rb = r0 + sb * (r1 - r0); | ||
1769 | |||
1770 | // use the average of the colors at the two circles | ||
1771 | for (k = 0; k < nComps; ++k) { | ||
1772 | colorA.c[k] = 0.5 * (colorA.c[k] + colorB.c[k]); | ||
1773 | } | ||
1774 | state->setFillColor(&colorA); | ||
1775 | out->updateFillColor(state); | ||
1776 | |||
1777 | // construct path for first circle | ||
1778 | state->moveTo(xa + ra, ya); | ||
1779 | for (k = 1; k < n; ++k) { | ||
1780 | angle = ((fouble)k / (fouble)n) * 2 * M_PI; | ||
1781 | state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); | ||
1782 | } | ||
1783 | state->closePath(); | ||
1784 | |||
1785 | // construct and append path for second circle | ||
1786 | state->moveTo(xb + rb, yb); | ||
1787 | for (k = 1; k < n; ++k) { | ||
1788 | angle = ((fouble)k / (fouble)n) * 2 * M_PI; | ||
1789 | state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); | ||
1790 | } | ||
1791 | state->closePath(); | ||
1792 | |||
1793 | // fill the ring | ||
1794 | out->eoFill(state); | ||
1795 | state->clearPath(); | ||
1796 | |||
1797 | // step to the next value of t | ||
1798 | ia = ib; | ||
1799 | sa = sb; | ||
1800 | ta = tb; | ||
1801 | xa = xb; | ||
1802 | ya = yb; | ||
1803 | ra = rb; | ||
1804 | colorA = colorB; | ||
1805 | } | ||
1806 | } | ||
1807 | |||
1622 | void Gfx::doEndPath() { | 1808 | void Gfx::doEndPath() { |
1623 | if (state->isPath() && clip != clipNone) { | 1809 | if (state->isPath() && clip != clipNone) { |
@@ -1801,4 +1987,5 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) { | |||
1801 | Array *a; | 1987 | Array *a; |
1802 | Object obj; | 1988 | Object obj; |
1989 | int wMode; | ||
1803 | int i; | 1990 | int i; |
1804 | 1991 | ||
@@ -1807,9 +1994,14 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) { | |||
1807 | return; | 1994 | return; |
1808 | } | 1995 | } |
1996 | wMode = state->getFont()->getWMode(); | ||
1809 | a = args[0].getArray(); | 1997 | a = args[0].getArray(); |
1810 | for (i = 0; i < a->getLength(); ++i) { | 1998 | for (i = 0; i < a->getLength(); ++i) { |
1811 | a->get(i, &obj); | 1999 | a->get(i, &obj); |
1812 | if (obj.isNum()) { | 2000 | if (obj.isNum()) { |
1813 | state->textShift(-obj.getNum() * 0.001 * state->getFontSize()); | 2001 | if (wMode) { |
2002 | state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize()); | ||
2003 | } else { | ||
2004 | state->textShift(-obj.getNum() * 0.001 * state->getFontSize(), 0); | ||
2005 | } | ||
1814 | out->updateTextShift(state, obj.getNum()); | 2006 | out->updateTextShift(state, obj.getNum()); |
1815 | } else if (obj.isString()) { | 2007 | } else if (obj.isString()) { |
@@ -1824,11 +2016,17 @@ void Gfx::opShowSpaceText(Object args[], int numArgs) { | |||
1824 | void Gfx::doShowText(GString *s) { | 2016 | void Gfx::doShowText(GString *s) { |
1825 | GfxFont *font; | 2017 | GfxFont *font; |
2018 | int wMode; | ||
1826 | fouble riseX, riseY; | 2019 | fouble riseX, riseY; |
1827 | CharCode code; | 2020 | CharCode code; |
1828 | Unicode u[8]; | 2021 | Unicode u[8]; |
1829 | fouble dx, dy, dx2, dy2, tdx, tdy; | 2022 | fouble x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy; |
1830 | fouble originX, originY, tOriginX, tOriginY; | 2023 | fouble originX, originY, tOriginX, tOriginY; |
2024 | fouble oldCTM[6], newCTM[6]; | ||
2025 | fouble *mat; | ||
2026 | Object charProc; | ||
2027 | Dict *resDict; | ||
2028 | Parser *oldParser; | ||
1831 | char *p; | 2029 | char *p; |
1832 | int len, n, uLen, nChars, nSpaces; | 2030 | int len, n, uLen, nChars, nSpaces, i; |
1833 | 2031 | ||
1834 | if (fontChanged) { | 2032 | if (fontChanged) { |
@@ -1837,16 +2035,12 @@ void Gfx::doShowText(GString *s) { | |||
1837 | } | 2035 | } |
1838 | font = state->getFont(); | 2036 | font = state->getFont(); |
2037 | wMode = font->getWMode(); | ||
1839 | 2038 | ||
1840 | #if 0 //~type3 | 2039 | if (out->useDrawChar()) { |
1841 | fouble x, y; | ||
1842 | fouble oldCTM[6], newCTM[6]; | ||
1843 | fouble *mat; | ||
1844 | Object charProc; | ||
1845 | Parser *oldParser; | ||
1846 | int i; | ||
1847 | |||
1848 | //~ also check out->renderType3() | ||
1849 | if (font->getType() == fontType3) { | ||
1850 | out->beginString(state, s); | 2040 | out->beginString(state, s); |
2041 | } | ||
2042 | |||
2043 | // handle a Type 3 char | ||
2044 | if (font->getType() == fontType3 && out->interpretType3Chars()) { | ||
1851 | mat = state->getCTM(); | 2045 | mat = state->getCTM(); |
1852 | for (i = 0; i < 6; ++i) { | 2046 | for (i = 0; i < 6; ++i) { |
@@ -1868,4 +2062,6 @@ void Gfx::doShowText(GString *s) { | |||
1868 | newCTM[2] *= state->getHorizScaling(); | 2062 | newCTM[2] *= state->getHorizScaling(); |
1869 | state->textTransformDelta(0, state->getRise(), &riseX, &riseY); | 2063 | state->textTransformDelta(0, state->getRise(), &riseX, &riseY); |
2064 | curX = state->getCurX(); | ||
2065 | curY = state->getCurY(); | ||
1870 | oldParser = parser; | 2066 | oldParser = parser; |
1871 | p = s->getCString(); | 2067 | p = s->getCString(); |
@@ -1875,19 +2071,4 @@ void Gfx::doShowText(GString *s) { | |||
1875 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, | 2071 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, |
1876 | &dx, &dy, &originX, &originY); | 2072 | &dx, &dy, &originX, &originY); |
1877 | state->transform(state->getCurX() + riseX, state->getCurY() + riseY, | ||
1878 | &x, &y); | ||
1879 | out->saveState(state); | ||
1880 | state = state->save(); | ||
1881 | state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y); | ||
1882 | //~ out->updateCTM(???) | ||
1883 | ((Gfx8BitFont *)font)->getCharProc(code, &charProc); | ||
1884 | if (charProc.isStream()) { | ||
1885 | display(&charProc, gFalse); | ||
1886 | } else { | ||
1887 | error(getPos(), "Missing or bad Type3 CharProc entry"); | ||
1888 | } | ||
1889 | state = state->restore(); | ||
1890 | out->restoreState(state); | ||
1891 | charProc.free(); | ||
1892 | dx = dx * state->getFontSize() + state->getCharSpace(); | 2073 | dx = dx * state->getFontSize() + state->getCharSpace(); |
1893 | if (n == 1 && *p == ' ') { | 2074 | if (n == 1 && *p == ' ') { |
@@ -1897,17 +2078,39 @@ void Gfx::doShowText(GString *s) { | |||
1897 | dy *= state->getFontSize(); | 2078 | dy *= state->getFontSize(); |
1898 | state->textTransformDelta(dx, dy, &tdx, &tdy); | 2079 | state->textTransformDelta(dx, dy, &tdx, &tdy); |
1899 | state->shift(tdx, tdy); | 2080 | state->transform(curX + riseX, curY + riseY, &x, &y); |
2081 | out->saveState(state); | ||
2082 | state = state->save(); | ||
2083 | state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y); | ||
2084 | //~ out->updateCTM(???) | ||
2085 | if (!out->beginType3Char(state, code, u, uLen)) { | ||
2086 | ((Gfx8BitFont *)font)->getCharProc(code, &charProc); | ||
2087 | if ((resDict = ((Gfx8BitFont *)font)->getResources())) { | ||
2088 | pushResources(resDict); | ||
2089 | } | ||
2090 | if (charProc.isStream()) { | ||
2091 | display(&charProc, gFalse); | ||
2092 | } else { | ||
2093 | error(getPos(), "Missing or bad Type3 CharProc entry"); | ||
2094 | } | ||
2095 | out->endType3Char(state); | ||
2096 | if (resDict) { | ||
2097 | popResources(); | ||
2098 | } | ||
2099 | charProc.free(); | ||
2100 | } | ||
2101 | state = state->restore(); | ||
2102 | out->restoreState(state); | ||
2103 | // GfxState::restore() does *not* restore the current position, | ||
2104 | // so we track it here with (curX, curY) | ||
2105 | curX += tdx; | ||
2106 | curY += tdy; | ||
2107 | state->moveTo(curX, curY); | ||
1900 | p += n; | 2108 | p += n; |
1901 | len -= n; | 2109 | len -= n; |
1902 | } | 2110 | } |
1903 | parser = oldParser; | 2111 | parser = oldParser; |
1904 | out->endString(state); | ||
1905 | return; | ||
1906 | } | ||
1907 | #endif | ||
1908 | 2112 | ||
1909 | if (out->useDrawChar()) { | 2113 | } else if (out->useDrawChar()) { |
1910 | state->textTransformDelta(0, state->getRise(), &riseX, &riseY); | 2114 | state->textTransformDelta(0, state->getRise(), &riseX, &riseY); |
1911 | out->beginString(state, s); | ||
1912 | p = s->getCString(); | 2115 | p = s->getCString(); |
1913 | len = s->getLength(); | 2116 | len = s->getLength(); |
@@ -1916,10 +2119,18 @@ void Gfx::doShowText(GString *s) { | |||
1916 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, | 2119 | u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, |
1917 | &dx, &dy, &originX, &originY); | 2120 | &dx, &dy, &originX, &originY); |
1918 | dx = dx * state->getFontSize() + state->getCharSpace(); | 2121 | if (wMode) { |
1919 | if (n == 1 && *p == ' ') { | 2122 | dx *= state->getFontSize(); |
1920 | dx += state->getWordSpace(); | 2123 | dy = dy * state->getFontSize() + state->getCharSpace(); |
2124 | if (n == 1 && *p == ' ') { | ||
2125 | dy += state->getWordSpace(); | ||
2126 | } | ||
2127 | } else { | ||
2128 | dx = dx * state->getFontSize() + state->getCharSpace(); | ||
2129 | if (n == 1 && *p == ' ') { | ||
2130 | dx += state->getWordSpace(); | ||
2131 | } | ||
2132 | dx *= state->getHorizScaling(); | ||
2133 | dy *= state->getFontSize(); | ||
1921 | } | 2134 | } |
1922 | dx *= state->getHorizScaling(); | ||
1923 | dy *= state->getFontSize(); | ||
1924 | state->textTransformDelta(dx, dy, &tdx, &tdy); | 2135 | state->textTransformDelta(dx, dy, &tdx, &tdy); |
1925 | originX *= state->getFontSize(); | 2136 | originX *= state->getFontSize(); |
@@ -1932,5 +2143,4 @@ void Gfx::doShowText(GString *s) { | |||
1932 | len -= n; | 2143 | len -= n; |
1933 | } | 2144 | } |
1934 | out->endString(state); | ||
1935 | 2145 | ||
1936 | } else { | 2146 | } else { |
@@ -1952,13 +2162,26 @@ void Gfx::doShowText(GString *s) { | |||
1952 | len -= n; | 2162 | len -= n; |
1953 | } | 2163 | } |
1954 | dx = dx * state->getFontSize() | 2164 | if (wMode) { |
1955 | + nChars * state->getCharSpace() | 2165 | dx *= state->getFontSize(); |
1956 | + nSpaces * state->getWordSpace(); | 2166 | dy = dy * state->getFontSize() |
1957 | dx *= state->getHorizScaling(); | 2167 | + nChars * state->getCharSpace() |
1958 | dy *= state->getFontSize(); | 2168 | + nSpaces * state->getWordSpace(); |
2169 | } else { | ||
2170 | dx = dx * state->getFontSize() | ||
2171 | + nChars * state->getCharSpace() | ||
2172 | + nSpaces * state->getWordSpace(); | ||
2173 | dx *= state->getHorizScaling(); | ||
2174 | dy *= state->getFontSize(); | ||
2175 | } | ||
1959 | state->textTransformDelta(dx, dy, &tdx, &tdy); | 2176 | state->textTransformDelta(dx, dy, &tdx, &tdy); |
1960 | out->drawString(state, s); | 2177 | out->drawString(state, s); |
1961 | state->shift(tdx, tdy); | 2178 | state->shift(tdx, tdy); |
1962 | } | 2179 | } |
2180 | |||
2181 | if (out->useDrawChar()) { | ||
2182 | out->endString(state); | ||
2183 | } | ||
2184 | |||
2185 | updateLevel += 10 * s->getLength(); | ||
1963 | } | 2186 | } |
1964 | 2187 | ||
@@ -1968,5 +2191,5 @@ void Gfx::doShowText(GString *s) { | |||
1968 | 2191 | ||
1969 | void Gfx::opXObject(Object args[], int numArgs) { | 2192 | void Gfx::opXObject(Object args[], int numArgs) { |
1970 | Object obj1, obj2, refObj; | 2193 | Object obj1, obj2, obj3, refObj; |
1971 | #if OPI_SUPPORT | 2194 | #if OPI_SUPPORT |
1972 | Object opiDict; | 2195 | Object opiDict; |
@@ -1994,4 +2217,8 @@ void Gfx::opXObject(Object args[], int numArgs) { | |||
1994 | } else if (obj2.isName("Form")) { | 2217 | } else if (obj2.isName("Form")) { |
1995 | doForm(&obj1); | 2218 | doForm(&obj1); |
2219 | } else if (obj2.isName("PS")) { | ||
2220 | obj1.streamGetDict()->lookup("Level1", &obj3); | ||
2221 | out->psXObject(obj1.getStream(), | ||
2222 | obj3.isStream() ? obj3.getStream() : (Stream *)NULL); | ||
1996 | } else if (obj2.isName()) { | 2223 | } else if (obj2.isName()) { |
1997 | error(getPos(), "Unknown XObject subtype '%s'", obj2.getName()); | 2224 | error(getPos(), "Unknown XObject subtype '%s'", obj2.getName()); |
@@ -2149,4 +2376,9 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) { | |||
2149 | } | 2376 | } |
2150 | 2377 | ||
2378 | if ((i = width * height) > 1000) { | ||
2379 | i = 1000; | ||
2380 | } | ||
2381 | updateLevel += i; | ||
2382 | |||
2151 | return; | 2383 | return; |
2152 | 2384 | ||
@@ -2216,11 +2448,14 @@ void Gfx::doForm(Object *str) { | |||
2216 | } | 2448 | } |
2217 | 2449 | ||
2218 | void Gfx::doWidgetForm(Object *str, fouble xMin, fouble yMin, | 2450 | void Gfx::doAnnot(Object *str, fouble xMin, fouble yMin, |
2219 | fouble xMax, fouble yMax) { | 2451 | fouble xMax, fouble yMax) { |
2220 | Dict *dict, *resDict; | 2452 | Dict *dict, *resDict; |
2221 | Object matrixObj, bboxObj, resObj; | 2453 | Object matrixObj, bboxObj, resObj; |
2222 | Object obj1; | 2454 | Object obj1; |
2223 | fouble m[6], bbox[6]; | 2455 | fouble m[6], bbox[6], ictm[6]; |
2224 | fouble sx, sy; | 2456 | fouble *ctm; |
2457 | fouble formX0, formY0, formX1, formY1; | ||
2458 | fouble annotX0, annotY0, annotX1, annotY1; | ||
2459 | fouble det, x, y, sx, sy; | ||
2225 | int i; | 2460 | int i; |
2226 | 2461 | ||
@@ -2228,5 +2463,5 @@ void Gfx::doWidgetForm(Object *str, fouble xMin, fouble yMin, | |||
2228 | dict = str->streamGetDict(); | 2463 | dict = str->streamGetDict(); |
2229 | 2464 | ||
2230 | // get bounding box | 2465 | // get the form bounding box |
2231 | dict->lookup("BBox", &bboxObj); | 2466 | dict->lookup("BBox", &bboxObj); |
2232 | if (!bboxObj.isArray()) { | 2467 | if (!bboxObj.isArray()) { |
@@ -2242,5 +2477,5 @@ void Gfx::doWidgetForm(Object *str, fouble xMin, fouble yMin, | |||
2242 | bboxObj.free(); | 2477 | bboxObj.free(); |
2243 | 2478 | ||
2244 | // get matrix | 2479 | // get the form matrix |
2245 | dict->lookup("Matrix", &matrixObj); | 2480 | dict->lookup("Matrix", &matrixObj); |
2246 | if (matrixObj.isArray()) { | 2481 | if (matrixObj.isArray()) { |
@@ -2257,14 +2492,62 @@ void Gfx::doWidgetForm(Object *str, fouble xMin, fouble yMin, | |||
2257 | matrixObj.free(); | 2492 | matrixObj.free(); |
2258 | 2493 | ||
2259 | // scale form bbox to widget rectangle | 2494 | // transform the form bbox from form space to user space |
2260 | sx = fabs((xMax - xMin) / (bbox[2] - bbox[0])); | 2495 | formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4]; |
2261 | sy = fabs((yMax - yMin) / (bbox[3] - bbox[1])); | 2496 | formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5]; |
2262 | m[0] *= sx; m[1] *= sy; | 2497 | formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4]; |
2263 | m[2] *= sx; m[3] *= sy; | 2498 | formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5]; |
2264 | m[4] *= sx; m[5] *= sy; | 2499 | |
2500 | // transform the annotation bbox from default user space to user | ||
2501 | // space: (bbox * baseMatrix) * iCTM | ||
2502 | ctm = state->getCTM(); | ||
2503 | det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]); | ||
2504 | ictm[0] = ctm[3] * det; | ||
2505 | ictm[1] = -ctm[1] * det; | ||
2506 | ictm[2] = -ctm[2] * det; | ||
2507 | ictm[3] = ctm[0] * det; | ||
2508 | ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; | ||
2509 | ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; | ||
2510 | x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4]; | ||
2511 | y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5]; | ||
2512 | annotX0 = ictm[0] * x + ictm[2] * y + ictm[4]; | ||
2513 | annotY0 = ictm[1] * x + ictm[3] * y + ictm[5]; | ||
2514 | x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4]; | ||
2515 | y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5]; | ||
2516 | annotX1 = ictm[0] * x + ictm[2] * y + ictm[4]; | ||
2517 | annotY1 = ictm[1] * x + ictm[3] * y + ictm[5]; | ||
2518 | |||
2519 | // swap min/max coords | ||
2520 | if (formX0 > formX1) { | ||
2521 | x = formX0; formX0 = formX1; formX1 = x; | ||
2522 | } | ||
2523 | if (formY0 > formY1) { | ||
2524 | y = formY0; formY0 = formY1; formY1 = y; | ||
2525 | } | ||
2526 | if (annotX0 > annotX1) { | ||
2527 | x = annotX0; annotX0 = annotX1; annotX1 = x; | ||
2528 | } | ||
2529 | if (annotY0 > annotY1) { | ||
2530 | y = annotY0; annotY0 = annotY1; annotY1 = y; | ||
2531 | } | ||
2265 | 2532 | ||
2266 | // translate to widget rectangle | 2533 | // scale the form to fit the annotation bbox |
2267 | m[4] += xMin; | 2534 | if (formX1 == formX0) { |
2268 | m[5] += yMin; | 2535 | // this shouldn't happen |
2536 | sx = 1; | ||
2537 | } else { | ||
2538 | sx = (annotX1 - annotX0) / (formX1 - formX0); | ||
2539 | } | ||
2540 | if (formY1 == formY0) { | ||
2541 | // this shouldn't happen | ||
2542 | sy = 1; | ||
2543 | } else { | ||
2544 | sy = (annotY1 - annotY0) / (formY1 - formY0); | ||
2545 | } | ||
2546 | m[0] *= sx; | ||
2547 | m[2] *= sx; | ||
2548 | m[4] = (m[4] - formX0) * sx + annotX0; | ||
2549 | m[1] *= sy; | ||
2550 | m[3] *= sy; | ||
2551 | m[5] = (m[5] - formY0) * sy + annotY0; | ||
2269 | 2552 | ||
2270 | // get resources | 2553 | // get resources |
@@ -2282,9 +2565,8 @@ void Gfx::doForm1(Object *str, Dict *resDict, fouble *matrix, fouble *bbox) { | |||
2282 | Parser *oldParser; | 2565 | Parser *oldParser; |
2283 | fouble oldBaseMatrix[6]; | 2566 | fouble oldBaseMatrix[6]; |
2284 | GfxResources *resPtr; | ||
2285 | int i; | 2567 | int i; |
2286 | 2568 | ||
2287 | // push new resources on stack | 2569 | // push new resources on stack |
2288 | res = new GfxResources(xref, resDict, res); | 2570 | pushResources(resDict); |
2289 | 2571 | ||
2290 | // save current graphics state | 2572 | // save current graphics state |
@@ -2333,9 +2615,19 @@ void Gfx::doForm1(Object *str, Dict *resDict, fouble *matrix, fouble *bbox) { | |||
2333 | 2615 | ||
2334 | // pop resource stack | 2616 | // pop resource stack |
2617 | popResources(); | ||
2618 | |||
2619 | return; | ||
2620 | } | ||
2621 | |||
2622 | void Gfx::pushResources(Dict *resDict) { | ||
2623 | res = new GfxResources(xref, resDict, res); | ||
2624 | } | ||
2625 | |||
2626 | void Gfx::popResources() { | ||
2627 | GfxResources *resPtr; | ||
2628 | |||
2335 | resPtr = res->getNext(); | 2629 | resPtr = res->getNext(); |
2336 | delete res; | 2630 | delete res; |
2337 | res = resPtr; | 2631 | res = resPtr; |
2338 | |||
2339 | return; | ||
2340 | } | 2632 | } |
2341 | 2633 | ||
@@ -2379,17 +2671,22 @@ Stream *Gfx::buildImageStream() { | |||
2379 | error(getPos(), "Inline image dictionary key must be a name object"); | 2671 | error(getPos(), "Inline image dictionary key must be a name object"); |
2380 | obj.free(); | 2672 | obj.free(); |
2381 | parser->getObj(&obj); | ||
2382 | } else { | 2673 | } else { |
2383 | key = copyString(obj.getName()); | 2674 | key = copyString(obj.getName()); |
2384 | obj.free(); | 2675 | obj.free(); |
2385 | parser->getObj(&obj); | 2676 | parser->getObj(&obj); |
2386 | if (obj.isEOF() || obj.isError()) | 2677 | if (obj.isEOF() || obj.isError()) { |
2678 | gfree(key); | ||
2387 | break; | 2679 | break; |
2680 | } | ||
2388 | dict.dictAdd(key, &obj); | 2681 | dict.dictAdd(key, &obj); |
2389 | } | 2682 | } |
2390 | parser->getObj(&obj); | 2683 | parser->getObj(&obj); |
2391 | } | 2684 | } |
2392 | if (obj.isEOF()) | 2685 | if (obj.isEOF()) { |
2393 | error(getPos(), "End of file in inline image"); | 2686 | error(getPos(), "End of file in inline image"); |
2687 | obj.free(); | ||
2688 | dict.free(); | ||
2689 | return NULL; | ||
2690 | } | ||
2394 | obj.free(); | 2691 | obj.free(); |
2395 | 2692 | ||
@@ -2414,9 +2711,11 @@ void Gfx::opEndImage(Object args[], int numArgs) { | |||
2414 | 2711 | ||
2415 | void Gfx::opSetCharWidth(Object args[], int numArgs) { | 2712 | void Gfx::opSetCharWidth(Object args[], int numArgs) { |
2416 | error(getPos(), "Encountered 'd0' operator in content stream"); | 2713 | out->type3D0(state, args[0].getNum(), args[1].getNum()); |
2417 | } | 2714 | } |
2418 | 2715 | ||
2419 | void Gfx::opSetCacheDevice(Object args[], int numArgs) { | 2716 | void Gfx::opSetCacheDevice(Object args[], int numArgs) { |
2420 | error(getPos(), "Encountered 'd1' operator in content stream"); | 2717 | out->type3D1(state, args[0].getNum(), args[1].getNum(), |
2718 | args[2].getNum(), args[3].getNum(), | ||
2719 | args[4].getNum(), args[5].getNum()); | ||
2421 | } | 2720 | } |
2422 | 2721 | ||