Lines 53-58
Link Here
|
53 |
import org.apache.batik.gvt.GraphicsNode; |
53 |
import org.apache.batik.gvt.GraphicsNode; |
54 |
|
54 |
|
55 |
import java.text.AttributedCharacterIterator; |
55 |
import java.text.AttributedCharacterIterator; |
|
|
56 |
import java.text.AttributedString; |
56 |
import java.text.CharacterIterator; |
57 |
import java.text.CharacterIterator; |
57 |
import java.awt.Graphics; |
58 |
import java.awt.Graphics; |
58 |
import java.awt.Graphics2D; |
59 |
import java.awt.Graphics2D; |
Lines 70-75
Link Here
|
70 |
import java.awt.Dimension; |
71 |
import java.awt.Dimension; |
71 |
import java.awt.BasicStroke; |
72 |
import java.awt.BasicStroke; |
72 |
import java.awt.AlphaComposite; |
73 |
import java.awt.AlphaComposite; |
|
|
74 |
import java.awt.font.TextAttribute; |
73 |
import java.awt.geom.AffineTransform; |
75 |
import java.awt.geom.AffineTransform; |
74 |
import java.awt.color.ColorSpace; |
76 |
import java.awt.color.ColorSpace; |
75 |
import java.awt.image.BufferedImage; |
77 |
import java.awt.image.BufferedImage; |
Lines 1397-1403
Link Here
|
1397 |
* @see #setClip |
1399 |
* @see #setClip |
1398 |
*/ |
1400 |
*/ |
1399 |
public void drawString(String s, float x, float y) { |
1401 |
public void drawString(String s, float x, float y) { |
1400 |
preparePainting(); |
1402 |
AttributedString attributedString = new AttributedString(s); |
1401 |
|
1403 |
|
1402 |
Font fontState; |
1404 |
Font fontState; |
1403 |
AffineTransform fontTransform = null; |
1405 |
AffineTransform fontTransform = null; |
Lines 1412-1553
Link Here
|
1412 |
String style = gFont.isItalic() ? "italic" : "normal"; |
1414 |
String style = gFont.isItalic() ? "italic" : "normal"; |
1413 |
int weight = gFont.isBold() ? Font.BOLD : Font.NORMAL; |
1415 |
int weight = gFont.isBold() ? Font.BOLD : Font.NORMAL; |
1414 |
FontTriplet triplet = fontInfo.fontLookup(n, style, weight); |
1416 |
FontTriplet triplet = fontInfo.fontLookup(n, style, weight); |
1415 |
fontState = fontInfo.getFontInstance(triplet, (int)(siz * 1000 + 0.5)); |
1417 |
fontState = fontInfo.getFontInstance(triplet, |
|
|
1418 |
(int) (siz * 1000 + 0.5)); |
1416 |
} else { |
1419 |
} else { |
1417 |
fontState = fontInfo.getFontInstance( |
1420 |
fontState = fontInfo.getFontInstance( |
1418 |
ovFontState.getFontTriplet(), ovFontState.getFontSize()); |
1421 |
ovFontState.getFontTriplet(), ovFontState.getFontSize()); |
1419 |
ovFontState = null; |
1422 |
ovFontState = null; |
|
|
1423 |
} |
1424 |
attributedString.addAttribute(TextAttribute.FONT, fontState); |
1425 |
if (fontTransform!=null) { |
1426 |
attributedString.addAttribute(TextAttribute.TRANSFORM, fontTransform); |
1420 |
} |
1427 |
} |
1421 |
String name; |
1428 |
|
1422 |
float size; |
1429 |
drawString(attributedString.getIterator(), x, y); |
1423 |
name = fontState.getFontName(); |
1430 |
|
1424 |
size = (float)fontState.getFontSize() / 1000f; |
|
|
1425 |
|
1426 |
if ((!name.equals(this.currentFontName)) |
1427 |
|| (size != this.currentFontSize)) { |
1428 |
this.currentFontName = name; |
1429 |
this.currentFontSize = size; |
1430 |
currentStream.write("/" + name + " " + size + " Tf\n"); |
1431 |
|
1432 |
} |
1433 |
|
1434 |
currentStream.write("q\n"); |
1435 |
|
1436 |
Color c = getColor(); |
1437 |
applyColor(c, true); |
1438 |
applyPaint(getPaint(), true); |
1439 |
int salpha = c.getAlpha(); |
1440 |
|
1441 |
if (salpha != 255) { |
1442 |
Map vals = new java.util.HashMap(); |
1443 |
vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(salpha / 255f)); |
1444 |
PDFGState gstate = pdfDoc.getFactory().makeGState( |
1445 |
vals, graphicsState.getGState()); |
1446 |
resourceContext.addGState(gstate); |
1447 |
currentStream.write("/" + gstate.getName() + " gs\n"); |
1448 |
} |
1449 |
|
1450 |
Map kerning = null; |
1451 |
boolean kerningAvailable = false; |
1452 |
|
1453 |
kerning = fontState.getKerning(); |
1454 |
if (kerning != null && !kerning.isEmpty()) { |
1455 |
kerningAvailable = true; |
1456 |
} |
1457 |
|
1458 |
// This assumes that *all* CIDFonts use a /ToUnicode mapping |
1459 |
boolean useMultiByte = false; |
1460 |
org.apache.fop.fonts.Typeface f = |
1461 |
(org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name); |
1462 |
if (f instanceof LazyFont) { |
1463 |
if (((LazyFont) f).getRealFont() instanceof CIDFont) { |
1464 |
useMultiByte = true; |
1465 |
} |
1466 |
} else if (f instanceof CIDFont) { |
1467 |
useMultiByte = true; |
1468 |
} |
1469 |
|
1470 |
// String startText = useMultiByte ? "<FEFF" : "("; |
1471 |
String startText = useMultiByte ? "<" : "("; |
1472 |
String endText = useMultiByte ? "> " : ") "; |
1473 |
|
1474 |
AffineTransform trans = getTransform(); |
1475 |
//trans.translate(x, y); |
1476 |
double[] vals = new double[6]; |
1477 |
trans.getMatrix(vals); |
1478 |
|
1479 |
concatMatrix(vals); |
1480 |
Shape imclip = getClip(); |
1481 |
writeClip(imclip); |
1482 |
|
1483 |
currentStream.write("BT\n"); |
1484 |
|
1485 |
AffineTransform localTransform = new AffineTransform(); |
1486 |
localTransform.translate(x, y); |
1487 |
if (fontTransform != null) { |
1488 |
localTransform.concatenate(fontTransform); |
1489 |
} |
1490 |
localTransform.scale(1, -1); |
1491 |
double[] lt = new double[6]; |
1492 |
localTransform.getMatrix(lt); |
1493 |
currentStream.write(PDFNumber.doubleOut(lt[0]) + " " |
1494 |
+ PDFNumber.doubleOut(lt[1]) + " " + PDFNumber.doubleOut(lt[2]) + " " |
1495 |
+ PDFNumber.doubleOut(lt[3]) + " " + PDFNumber.doubleOut(lt[4]) + " " |
1496 |
+ PDFNumber.doubleOut(lt[5]) + " Tm [" + startText); |
1497 |
|
1498 |
int l = s.length(); |
1499 |
|
1500 |
for (int i = 0; i < l; i++) { |
1501 |
char ch = fontState.mapChar(s.charAt(i)); |
1502 |
|
1503 |
if (!useMultiByte) { |
1504 |
if (ch > 127) { |
1505 |
currentStream.write("\\"); |
1506 |
currentStream.write(Integer.toOctalString((int)ch)); |
1507 |
} else { |
1508 |
switch (ch) { |
1509 |
case '(': |
1510 |
case ')': |
1511 |
case '\\': |
1512 |
currentStream.write("\\"); |
1513 |
break; |
1514 |
} |
1515 |
currentStream.write(ch); |
1516 |
} |
1517 |
} else { |
1518 |
currentStream.write(PDFText.toUnicodeHex(ch)); |
1519 |
} |
1520 |
|
1521 |
if (kerningAvailable && (i + 1) < l) { |
1522 |
addKerning(currentStream, (new Integer((int)ch)), |
1523 |
(new Integer((int)fontState.mapChar(s.charAt(i + 1)))), |
1524 |
kerning, startText, endText); |
1525 |
} |
1526 |
|
1527 |
} |
1528 |
currentStream.write(endText); |
1529 |
|
1530 |
|
1531 |
currentStream.write("] TJ\n"); |
1532 |
|
1533 |
currentStream.write("ET\n"); |
1534 |
currentStream.write("Q\n"); |
1535 |
} |
1431 |
} |
1536 |
|
1432 |
|
1537 |
private void addKerning(StringWriter buf, Integer ch1, Integer ch2, |
|
|
1538 |
Map kerning, String startText, |
1539 |
String endText) { |
1540 |
preparePainting(); |
1541 |
Map kernPair = (Map)kerning.get(ch1); |
1542 |
|
1543 |
if (kernPair != null) { |
1544 |
Integer width = (Integer)kernPair.get(ch2); |
1545 |
if (width != null) { |
1546 |
currentStream.write(endText + (-width.intValue()) + " " + startText); |
1547 |
} |
1548 |
} |
1549 |
} |
1550 |
|
1551 |
/** |
1433 |
/** |
1552 |
* Renders the text of the specified iterator, using the |
1434 |
* Renders the text of the specified iterator, using the |
1553 |
* <code>Graphics2D</code> context's current <code>Paint</code>. The |
1435 |
* <code>Graphics2D</code> context's current <code>Paint</code>. The |
Lines 1576-1621
Link Here
|
1576 |
public void drawString(AttributedCharacterIterator iterator, float x, |
1458 |
public void drawString(AttributedCharacterIterator iterator, float x, |
1577 |
float y) { |
1459 |
float y) { |
1578 |
preparePainting(); |
1460 |
preparePainting(); |
|
|
1461 |
currentStream.write("q\n"); |
1579 |
|
1462 |
|
1580 |
Font fontState = null; |
1463 |
Font fontState = null; |
1581 |
|
1464 |
|
|
|
1465 |
AffineTransform trans = getTransform(); |
1466 |
double[] vals = new double[6]; |
1467 |
trans.getMatrix(vals); |
1468 |
|
1469 |
concatMatrix(vals); |
1470 |
|
1582 |
Shape imclip = getClip(); |
1471 |
Shape imclip = getClip(); |
1583 |
writeClip(imclip); |
1472 |
writeClip(imclip); |
1584 |
Color c = getColor(); |
1473 |
Color c = getColor(); |
1585 |
applyColor(c, true); |
1474 |
applyColor(c, true); |
1586 |
applyPaint(getPaint(), true); |
1475 |
applyPaint(getPaint(), true); |
|
|
1476 |
int salpha = c.getAlpha(); |
1587 |
|
1477 |
|
1588 |
boolean fill = true; |
1478 |
if (salpha != 255) { |
1589 |
boolean stroke = false; |
1479 |
Map alphaVals = new java.util.HashMap(); |
1590 |
if (true) { |
1480 |
alphaVals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(salpha / 255f)); |
1591 |
Stroke currentStroke = getStroke(); |
1481 |
PDFGState gstate = pdfDoc.getFactory().makeGState( |
1592 |
stroke = true; |
1482 |
alphaVals, graphicsState.getGState()); |
1593 |
applyStroke(currentStroke); |
1483 |
resourceContext.addGState(gstate); |
1594 |
applyColor(c, false); |
1484 |
currentStream.write("/" + gstate.getName() + " gs\n"); |
1595 |
applyPaint(getPaint(), false); |
|
|
1596 |
} |
1485 |
} |
|
|
1486 |
|
1487 |
/* Stroke was NOT implemented in the original drawString. |
1488 |
* Enabling this (and the next code segment) will result |
1489 |
* in all font being rendered as bold. |
1490 |
* This should probably be adapted to whatever the font |
1491 |
* weight is. |
1492 |
*/ |
1493 |
|
1494 |
// boolean fill = true; |
1495 |
// boolean stroke = false; |
1496 |
// if (false) { |
1497 |
// Stroke currentStroke = getStroke(); |
1498 |
// stroke = true; |
1499 |
// System.out.println(currentStroke.toString()); |
1500 |
// applyStroke(currentStroke); |
1501 |
// applyColor(c, false); |
1502 |
// applyPaint(getPaint(), false); |
1503 |
// } |
1597 |
|
1504 |
|
1598 |
currentStream.write("BT\n"); |
1505 |
currentStream.write("BT\n"); |
1599 |
|
1506 |
|
1600 |
// set text rendering mode: |
1507 |
/* Stroke was NOT implemented in the original drawString */ |
1601 |
// 0 - fill, 1 - stroke, 2 - fill then stroke |
1508 |
// // set text rendering mode: |
1602 |
int textr = 0; |
1509 |
// // 0 - fill, 1 - stroke, 2 - fill then stroke |
1603 |
if (fill && stroke) { |
1510 |
// int textr = 0; |
1604 |
textr = 2; |
1511 |
// if (fill && stroke) { |
1605 |
} else if (stroke) { |
1512 |
// textr = 2; |
1606 |
textr = 1; |
1513 |
// } else if (stroke) { |
1607 |
} |
1514 |
// textr = 1; |
1608 |
currentStream.write(textr + " Tr\n"); |
1515 |
// } |
|
|
1516 |
// currentStream.write(textr + " Tr\n"); |
1517 |
// |
1518 |
char unicodeChar = iterator.first(); |
1519 |
while (unicodeChar != CharacterIterator.DONE) { |
1520 |
Map attr = iterator.getAttributes(); |
1521 |
Object fontAttr = (Font)attr.get(TextAttribute.FONT); |
1522 |
/* TODO: fontAttr could theoretically be a java.awt.Font */ |
1523 |
fontState = (Font)fontAttr; |
1524 |
|
1525 |
if (!fontState.hasChar(unicodeChar)) { |
1526 |
fontState = tryToGetAFontThatHasCHar(unicodeChar, fontState); |
1527 |
} |
1528 |
|
1529 |
AffineTransform fontTransform = (AffineTransform)attr.get(TextAttribute.TRANSFORM); |
1609 |
|
1530 |
|
1610 |
AffineTransform trans = getTransform(); |
|
|
1611 |
trans.translate(x, y); |
1612 |
double[] vals = new double[6]; |
1613 |
trans.getMatrix(vals); |
1614 |
|
1615 |
for (char ch = iterator.first(); ch != CharacterIterator.DONE; |
1616 |
ch = iterator.next()) { |
1617 |
//Map attr = iterator.getAttributes(); |
1618 |
|
1619 |
String name = fontState.getFontName(); |
1531 |
String name = fontState.getFontName(); |
1620 |
int size = fontState.getFontSize(); |
1532 |
int size = fontState.getFontSize(); |
1621 |
if ((!name.equals(this.currentFontName)) |
1533 |
if ((!name.equals(this.currentFontName)) |
Lines 1624-1650
Link Here
|
1624 |
this.currentFontSize = size; |
1536 |
this.currentFontSize = size; |
1625 |
currentStream.write("/" + name + " " + (size / 1000) |
1537 |
currentStream.write("/" + name + " " + (size / 1000) |
1626 |
+ " Tf\n"); |
1538 |
+ " Tf\n"); |
|
|
1539 |
} |
1540 |
|
1541 |
/* |
1542 |
* TODO: If the font is the same as for the last character, there |
1543 |
* could be some optimization (not closing the string, not repeating |
1544 |
* the transformations |
1545 |
*/ |
1546 |
|
1547 |
Map kerning = null; |
1548 |
boolean kerningAvailable = false; |
1627 |
|
1549 |
|
|
|
1550 |
kerning = fontState.getKerning(); |
1551 |
if (kerning != null && !kerning.isEmpty()) { |
1552 |
kerningAvailable = true; |
1628 |
} |
1553 |
} |
|
|
1554 |
|
1555 |
// This assumes that *all* CIDFonts use a /ToUnicode mapping |
1556 |
boolean useMultiByte = false; |
1557 |
org.apache.fop.fonts.Typeface f = |
1558 |
(org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name); |
1559 |
if (f instanceof LazyFont) { |
1560 |
if (((LazyFont) f).getRealFont() instanceof CIDFont) { |
1561 |
useMultiByte = true; |
1562 |
} |
1563 |
} else if (f instanceof CIDFont) { |
1564 |
useMultiByte = true; |
1565 |
} |
1566 |
|
1567 |
String startText = useMultiByte ? "<" : "("; |
1568 |
String endText = useMultiByte ? "> " : ") "; |
1569 |
|
1570 |
currentStream.write("BT\n"); |
1629 |
|
1571 |
|
1630 |
currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " " |
1572 |
AffineTransform localTransform = new AffineTransform(); |
1631 |
+ PDFNumber.doubleOut(vals[1], DEC) + " " |
1573 |
localTransform.translate(x, y); |
1632 |
+ PDFNumber.doubleOut(vals[2], DEC) + " " |
1574 |
if (fontTransform != null) { |
1633 |
+ PDFNumber.doubleOut(vals[3], DEC) + " " |
1575 |
//localTransform.concatenate(fontTransform); |
1634 |
+ PDFNumber.doubleOut(vals[4], DEC) + " " |
1576 |
} |
1635 |
+ PDFNumber.doubleOut(vals[5], DEC) + " Tm (" + ch |
1577 |
localTransform.scale(1, -1); |
1636 |
+ ") Tj\n"); |
1578 |
double[] lt = new double[6]; |
|
|
1579 |
localTransform.getMatrix(lt); |
1580 |
currentStream.write(PDFNumber.doubleOut(lt[0]) + " " |
1581 |
+ PDFNumber.doubleOut(lt[1]) + " " + PDFNumber.doubleOut(lt[2]) + " " |
1582 |
+ PDFNumber.doubleOut(lt[3]) + " " |
1583 |
+ PDFNumber.doubleOut(lt[4]) + " " |
1584 |
+ PDFNumber.doubleOut(lt[5]) + " Tm [" + startText); |
1585 |
x += fontState.getCharWidth(unicodeChar) / 1000f; |
1586 |
|
1587 |
char fontChar = fontState.mapChar(unicodeChar); |
1588 |
|
1589 |
if (!useMultiByte) { |
1590 |
if (fontChar > 127) { |
1591 |
currentStream.write("\\"); |
1592 |
currentStream.write(Integer.toOctalString((int) fontChar)); |
1593 |
} else { |
1594 |
switch (fontChar) { |
1595 |
case '(': |
1596 |
case ')': |
1597 |
case '\\': |
1598 |
currentStream.write("\\"); |
1599 |
break; |
1600 |
} |
1601 |
currentStream.write(fontChar); |
1602 |
} |
1603 |
} else { |
1604 |
currentStream.write(PDFText.toUnicodeHex(fontChar)); |
1605 |
} |
1606 |
currentStream.write(endText); |
1607 |
currentStream.write("] TJ\n"); |
1608 |
|
1609 |
char nextUnicodeChar = iterator.next(); |
1610 |
|
1611 |
if (kerningAvailable && (nextUnicodeChar!= CharacterIterator.DONE)) { |
1612 |
x += fontState.getKernValue(fontChar,fontState.mapChar(nextUnicodeChar)) / 1000f; |
1613 |
} |
1614 |
unicodeChar = nextUnicodeChar; |
1637 |
} |
1615 |
} |
1638 |
|
1616 |
|
1639 |
currentStream.write("ET\n"); |
1617 |
currentStream.write("ET\n"); |
|
|
1618 |
this.currentFontName = ""; |
1619 |
currentStream.write("Q\n"); |
1640 |
} |
1620 |
} |
1641 |
|
1621 |
|
1642 |
/** |
1622 |
/** |
|
|
1623 |
* returns a Font that contains the given glyph. |
1624 |
* <p> |
1625 |
* This is a proof-of-concept implementation. This is probably the wrong |
1626 |
* place for this. Also, hardcoding names like "Symbol" and "ZapfDingbats" |
1627 |
* is a bad idea. Ideally this would aquire a list of fonts similar to the |
1628 |
* original one and then get the best fit. |
1629 |
* |
1630 |
* @param unicodeChar |
1631 |
* the character to look for. |
1632 |
* @param fontState |
1633 |
* the original font information. |
1634 |
* @return |
1635 |
*/ |
1636 |
private Font tryToGetAFontThatHasCHar(char unicodeChar, Font fontState) { |
1637 |
FontTriplet originalTriplet = fontState.getFontTriplet(); |
1638 |
|
1639 |
Font symbolFont = fontInfo.getFontInstance(fontInfo.fontLookup("Symbol", |
1640 |
originalTriplet.getStyle(), originalTriplet.getWeight()), |
1641 |
fontState.getFontSize()); |
1642 |
if (symbolFont.hasChar(unicodeChar)) |
1643 |
return symbolFont; |
1644 |
|
1645 |
Font dingbats = fontInfo.getFontInstance(fontInfo.fontLookup("ZapfDingbats", |
1646 |
originalTriplet.getStyle(), originalTriplet.getWeight()), |
1647 |
fontState.getFontSize()); |
1648 |
if (dingbats.hasChar(unicodeChar)) |
1649 |
return dingbats; |
1650 |
|
1651 |
return fontState; |
1652 |
} |
1653 |
|
1654 |
/** |
1643 |
* Fills the interior of a <code>Shape</code> using the settings of the |
1655 |
* Fills the interior of a <code>Shape</code> using the settings of the |
1644 |
* <code>Graphics2D</code> context. The rendering attributes applied |
1656 |
* <code>Graphics2D</code> context. The rendering attributes applied |
1645 |
* include the <code>Clip</code>, <code>Transform</code>, |
1657 |
* include the <code>Clip</code>, <code>Transform</code>, |
1646 |
* <code>Paint</code>, and <code>Composite</code>. |
1658 |
* <code>Paint</code>, and <code>Composite</code>. |
1647 |
* @param s the <code>Shape</code> to be filled |
1659 |
* |
|
|
1660 |
* @param s |
1661 |
* the <code>Shape</code> to be filled |
1648 |
* @see #setPaint |
1662 |
* @see #setPaint |
1649 |
* @see java.awt.Graphics#setColor |
1663 |
* @see java.awt.Graphics#setColor |
1650 |
* @see #transform |
1664 |
* @see #transform |