View | Details | Raw Unified | Return to bug 39422
Collapse All | Expand All

(-)src/java/org/apache/fop/svg/PDFGraphics2D.java (-164 / +178 lines)
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

Return to bug 39422