1
//========================================================================
5
// Copyright 2003 Glyph & Cog, LLC
7
//========================================================================
9
//========================================================================
11
// Modified under the Poppler project - http://poppler.freedesktop.org
13
// All changes made under the Poppler project to this file are licensed
14
// under GPL version 2 or later
16
// Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
17
// Copyright (C) 2005-2009 Albert Astals Cid <aacid@kde.org>
18
// Copyright (C) 2008 Pino Toscano <pino@kde.org>
19
// Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
20
// Copyright (C) 2009 Petr Gajdos <pgajdos@novell.com>
22
// To see a description of the changes please see the Changelog file that
23
// came with your tarball or type make ChangeLog if you are building from git
25
//========================================================================
29
#ifdef USE_GCC_PRAGMAS
30
#pragma implementation
36
#include "goo/gfile.h"
37
#include "GlobalParams.h"
43
#include "CharCodeToUnicode.h"
44
#include "FontEncodingTables.h"
45
#include <fofi/FoFiTrueType.h>
46
#include "ArthurOutputDev.h"
48
#include <QtCore/QtDebug>
49
#include <QtGui/QPainterPath>
50
//------------------------------------------------------------------------
52
#include "splash/SplashFontFileID.h"
53
#include "splash/SplashFontFile.h"
54
#include "splash/SplashFontEngine.h"
55
#include "splash/SplashFont.h"
56
#include "splash/SplashMath.h"
57
#include "splash/SplashPath.h"
58
#include "splash/SplashGlyphBitmap.h"
59
//------------------------------------------------------------------------
60
// SplashOutFontFileID
61
//------------------------------------------------------------------------
63
class SplashOutFontFileID: public SplashFontFileID {
66
SplashOutFontFileID(Ref *rA) { r = *rA; }
68
~SplashOutFontFileID() {}
70
GBool matches(SplashFontFileID *id) {
71
return ((SplashOutFontFileID *)id)->r.num == r.num &&
72
((SplashOutFontFileID *)id)->r.gen == r.gen;
82
//------------------------------------------------------------------------
84
//------------------------------------------------------------------------
86
ArthurOutputDev::ArthurOutputDev(QPainter *painter):
89
m_currentBrush = QBrush(Qt::SolidPattern);
95
ArthurOutputDev::~ArthurOutputDev()
100
void ArthurOutputDev::startDoc(XRef *xrefA) {
103
m_fontEngine = new SplashFontEngine(
105
globalParams->getEnableT1lib(),
107
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
108
globalParams->getEnableFreeType(),
111
m_painter->testRenderHint(QPainter::TextAntialiasing));
114
void ArthurOutputDev::startPage(int pageNum, GfxState *state)
116
// fill page with white background.
117
int w = static_cast<int>(state->getPageWidth());
118
int h = static_cast<int>(state->getPageHeight());
119
QColor fillColour(Qt::white);
120
QBrush fill(fillColour);
122
m_painter->setPen(fillColour);
123
m_painter->setBrush(fill);
124
m_painter->drawRect(0, 0, w, h);
125
m_painter->restore();
128
void ArthurOutputDev::endPage() {
131
void ArthurOutputDev::drawLink(Link *link, Catalog *catalog)
135
void ArthurOutputDev::saveState(GfxState *state)
140
void ArthurOutputDev::restoreState(GfxState *state)
142
m_painter->restore();
145
void ArthurOutputDev::updateAll(GfxState *state)
147
OutputDev::updateAll(state);
148
m_needFontUpdate = gTrue;
151
// This looks wrong - why aren't adjusting the matrix?
152
void ArthurOutputDev::updateCTM(GfxState *state, double m11, double m12,
153
double m21, double m22,
154
double m31, double m32)
156
updateLineDash(state);
157
updateLineJoin(state);
158
updateLineCap(state);
159
updateLineWidth(state);
162
void ArthurOutputDev::updateLineDash(GfxState *state)
164
// qDebug() << "updateLineDash";
167
void ArthurOutputDev::updateFlatness(GfxState *state)
169
// qDebug() << "updateFlatness";
172
void ArthurOutputDev::updateLineJoin(GfxState *state)
174
switch (state->getLineJoin()) {
176
m_currentPen.setJoinStyle(Qt::MiterJoin);
179
m_currentPen.setJoinStyle(Qt::RoundJoin);
182
m_currentPen.setJoinStyle(Qt::BevelJoin);
185
m_painter->setPen(m_currentPen);
188
void ArthurOutputDev::updateLineCap(GfxState *state)
190
switch (state->getLineCap()) {
192
m_currentPen.setCapStyle(Qt::FlatCap);
195
m_currentPen.setCapStyle(Qt::RoundCap);
198
m_currentPen.setCapStyle(Qt::SquareCap);
201
m_painter->setPen(m_currentPen);
204
void ArthurOutputDev::updateMiterLimit(GfxState *state)
206
// We can't do mitre (or Miter) limit with Qt4 yet.
207
// the limit is in state->getMiterLimit() when we get there
210
void ArthurOutputDev::updateLineWidth(GfxState *state)
212
m_currentPen.setWidthF(state->getTransformedLineWidth());
213
m_painter->setPen(m_currentPen);
216
void ArthurOutputDev::updateFillColor(GfxState *state)
219
QColor brushColour = m_currentBrush.color();
220
state->getFillRGB(&rgb);
221
brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), brushColour.alphaF());
222
m_currentBrush.setColor(brushColour);
225
void ArthurOutputDev::updateStrokeColor(GfxState *state)
228
QColor penColour = m_currentPen.color();
229
state->getStrokeRGB(&rgb);
230
penColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), penColour.alphaF());
231
m_currentPen.setColor(penColour);
232
m_painter->setPen(m_currentPen);
235
void ArthurOutputDev::updateFillOpacity(GfxState *state)
237
QColor brushColour= m_currentBrush.color();
238
brushColour.setAlphaF(state->getFillOpacity());
239
m_currentBrush.setColor(brushColour);
242
void ArthurOutputDev::updateStrokeOpacity(GfxState *state)
244
QColor penColour= m_currentPen.color();
245
penColour.setAlphaF(state->getStrokeOpacity());
246
m_currentPen.setColor(penColour);
247
m_painter->setPen(m_currentPen);
250
void ArthurOutputDev::updateFont(GfxState *state)
253
GfxFontType fontType;
254
SplashOutFontFileID *id;
255
SplashFontFile *fontFile;
256
SplashFontSrc *fontsrc;
259
Object refObj, strObj;
264
DisplayFontParam *dfp;
266
double m11, m12, m21, m22, fontSize;
270
SplashCoord matrix[6];
272
m_needFontUpdate = false;
278
if (!(gfxFont = state->getFont())) {
281
fontType = gfxFont->getType();
282
if (fontType == fontType3) {
286
// check the font file cache
287
id = new SplashOutFontFileID(gfxFont->getID());
288
if ((fontFile = m_fontEngine->getFontFile(id))) {
293
// if there is an embedded font, write it to disk
294
if (gfxFont->getEmbeddedFontID(&embRef)) {
295
tmpBuf = gfxFont->readEmbFontFile(xref, &tmpBufLen);
298
// if there is an external font file, use it
299
} else if (!(fileName = gfxFont->getExtFontFile())) {
301
// look for a display font mapping or a substitute font
303
if (gfxFont->getName()) {
304
dfp = globalParams->getDisplayFont(gfxFont);
307
error(-1, "Couldn't find a font for '%s'",
308
gfxFont->getName() ? gfxFont->getName()->getCString()
314
fileName = dfp->t1.fileName;
315
fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
318
fileName = dfp->tt.fileName;
319
fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
320
faceIndex = dfp->tt.faceIndex;
325
fontsrc = new SplashFontSrc;
327
fontsrc->setFile(fileName, gFalse);
329
fontsrc->setBuf(tmpBuf, tmpBufLen, gFalse);
331
// load the font file
334
if (!(fontFile = m_fontEngine->loadType1Font(
337
((Gfx8BitFont *)gfxFont)->getEncoding()))) {
338
error(-1, "Couldn't create a font for '%s'",
339
gfxFont->getName() ? gfxFont->getName()->getCString()
345
if (!(fontFile = m_fontEngine->loadType1CFont(
348
((Gfx8BitFont *)gfxFont)->getEncoding()))) {
349
error(-1, "Couldn't create a font for '%s'",
350
gfxFont->getName() ? gfxFont->getName()->getCString()
356
if (!(fontFile = m_fontEngine->loadOpenTypeT1CFont(
359
((Gfx8BitFont *)gfxFont)->getEncoding()))) {
360
error(-1, "Couldn't create a font for '%s'",
361
gfxFont->getName() ? gfxFont->getName()->getCString()
369
ff = FoFiTrueType::load(fileName->getCString());
371
ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
373
codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
380
if (!(fontFile = m_fontEngine->loadTrueTypeFont(
384
error(-1, "Couldn't create a font for '%s'",
385
gfxFont->getName() ? gfxFont->getName()->getCString()
392
if (!(fontFile = m_fontEngine->loadCIDFont(
395
error(-1, "Couldn't create a font for '%s'",
396
gfxFont->getName() ? gfxFont->getName()->getCString()
401
case fontCIDType0COT:
402
if (!(fontFile = m_fontEngine->loadOpenTypeCFFFont(
405
error(-1, "Couldn't create a font for '%s'",
406
gfxFont->getName() ? gfxFont->getName()->getCString()
415
if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
416
n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
418
codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
419
memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
420
n * sizeof(Gushort));
424
ff = FoFiTrueType::load(fileName->getCString());
426
ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
429
codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
432
if (!(fontFile = m_fontEngine->loadTrueTypeFont(
435
codeToGID, n, faceIndex))) {
436
error(-1, "Couldn't create a font for '%s'",
437
gfxFont->getName() ? gfxFont->getName()->getCString()
443
// this shouldn't happen
448
// get the font matrix
449
textMat = state->getTextMat();
450
fontSize = state->getFontSize();
451
m11 = textMat[0] * fontSize * state->getHorizScaling();
452
m12 = textMat[1] * fontSize * state->getHorizScaling();
453
m21 = textMat[2] * fontSize;
454
m22 = textMat[3] * fontSize;
457
QMatrix painterMatrix = m_painter->worldMatrix();
458
matrix[0] = painterMatrix.m11();
459
matrix[1] = painterMatrix.m12();
460
matrix[2] = painterMatrix.m21();
461
matrix[3] = painterMatrix.m22();
462
matrix[4] = painterMatrix.dx();
463
matrix[5] = painterMatrix.dy();
466
// create the scaled font
467
mat[0] = m11; mat[1] = -m12;
468
mat[2] = m21; mat[3] = -m22;
469
m_font = m_fontEngine->getFont(fontFile, mat, matrix);
479
static QPainterPath convertPath(GfxState *state, GfxPath *path, Qt::FillRule fillRule)
482
double x1, y1, x2, y2, x3, y3;
486
qPath.setFillRule(fillRule);
487
for (i = 0; i < path->getNumSubpaths(); ++i) {
488
subpath = path->getSubpath(i);
489
if (subpath->getNumPoints() > 0) {
490
state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
491
qPath.moveTo(x1, y1);
493
while (j < subpath->getNumPoints()) {
494
if (subpath->getCurve(j)) {
495
state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
496
state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
497
state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
498
qPath.cubicTo( x1, y1, x2, y2, x3, y3);
501
state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
502
qPath.lineTo(x1, y1);
506
if (subpath->isClosed()) {
507
qPath.closeSubpath();
514
void ArthurOutputDev::stroke(GfxState *state)
516
m_painter->drawPath( convertPath( state, state->getPath(), Qt::OddEvenFill ) );
519
void ArthurOutputDev::fill(GfxState *state)
521
m_painter->fillPath( convertPath( state, state->getPath(), Qt::WindingFill ), m_currentBrush );
524
void ArthurOutputDev::eoFill(GfxState *state)
526
m_painter->fillPath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentBrush );
529
void ArthurOutputDev::clip(GfxState *state)
531
m_painter->setClipPath(convertPath( state, state->getPath(), Qt::WindingFill ) );
534
void ArthurOutputDev::eoClip(GfxState *state)
536
m_painter->setClipPath(convertPath( state, state->getPath(), Qt::OddEvenFill ) );
539
void ArthurOutputDev::drawChar(GfxState *state, double x, double y,
540
double dx, double dy,
541
double originX, double originY,
542
CharCode code, int nBytes, Unicode *u, int uLen) {
547
if (m_needFontUpdate) {
554
// check for invisible text -- this is used by Acrobat Capture
555
render = state->getRender();
562
state->transform(x, y, &x1, &y1);
566
int x0, y0, xFrac, yFrac;
568
x0 = static_cast<int>(floor(x1));
569
xFrac = splashFloor((x1 - x0) * splashFontFraction);
570
y0 = static_cast<int>(floor(y1));
571
yFrac = splashFloor((y1 - y0) * splashFontFraction);
572
SplashPath * fontPath;
573
fontPath = m_font->getGlyphPath(code);
576
qPath.setFillRule(Qt::WindingFill);
577
for (int i = 0; i < fontPath->length; ++i) {
578
if (fontPath->flags[i] & splashPathFirst) {
579
qPath.moveTo(fontPath->pts[i].x+x0, fontPath->pts[i].y+y0);
580
} else if (fontPath->flags[i] & splashPathCurve) {
581
qPath.quadTo(fontPath->pts[i].x+x0, fontPath->pts[i].y+y0,
582
fontPath->pts[i+1].x+x0, fontPath->pts[i+1].y+y0);
588
// else if (fontPath->flags[i] & splashPathArcCW) {
589
// qDebug() << "Need to implement arc";
592
qPath.lineTo(fontPath->pts[i].x+x0, fontPath->pts[i].y+y0);
594
if (fontPath->flags[i] & splashPathLast) {
595
qPath.closeSubpath();
600
QColor brushColour = m_currentBrush.color();
601
state->getFillRGB(&rgb);
602
brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity());
603
m_painter->setBrush(brushColour);
604
QColor penColour = m_currentPen.color();
605
state->getStrokeRGB(&rgb);
606
penColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getStrokeOpacity());
607
m_painter->setPen(penColour);
608
m_painter->drawPath( qPath );
609
m_painter->restore();
614
if ((render & 3) == 1 || (render & 3) == 2) {
615
qDebug() << "no stroke";
617
if ((path = m_font->getGlyphPath(code))) {
618
path->offset((SplashCoord)x1, (SplashCoord)y1);
619
splash->stroke(path);
627
qDebug() << "no clip";
629
path = m_font->getGlyphPath(code);
630
path->offset((SplashCoord)x1, (SplashCoord)y1);
632
textClipPath->append(path);
641
GBool ArthurOutputDev::beginType3Char(GfxState *state, double x, double y,
642
double dx, double dy,
643
CharCode code, Unicode *u, int uLen)
648
void ArthurOutputDev::endType3Char(GfxState *state)
652
void ArthurOutputDev::type3D0(GfxState *state, double wx, double wy)
656
void ArthurOutputDev::type3D1(GfxState *state, double wx, double wy,
657
double llx, double lly, double urx, double ury)
661
void ArthurOutputDev::endTextObject(GfxState *state)
666
void ArthurOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
667
int width, int height, GBool invert,
668
GBool interpolate, GBool inlineImg)
670
qDebug() << "drawImageMask";
672
unsigned char *buffer;
674
cairo_surface_t *image;
675
cairo_pattern_t *pattern;
680
cairo_matrix_t matrix;
684
row_stride = (width + 3) & ~3;
685
buffer = (unsigned char *) malloc (height * row_stride);
686
if (buffer == NULL) {
687
error(-1, "Unable to allocate memory for image.");
691
/* TODO: Do we want to cache these? */
692
imgStr = new ImageStream(str, width, 1, 1);
695
invert_bit = invert ? 1 : 0;
697
for (y = 0; y < height; y++) {
698
pix = imgStr->getLine();
699
dest = buffer + y * row_stride;
700
for (x = 0; x < width; x++) {
702
if (pix[x] ^ invert_bit)
709
image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_A8,
710
width, height, row_stride);
713
pattern = cairo_pattern_create_for_surface (image);
717
ctm = state->getCTM();
718
LOG (printf ("drawImageMask %dx%d, matrix: %f, %f, %f, %f, %f, %f\n",
719
width, height, ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]));
720
matrix.xx = ctm[0] / width;
721
matrix.xy = -ctm[2] / height;
722
matrix.yx = ctm[1] / width;
723
matrix.yy = -ctm[3] / height;
724
matrix.x0 = ctm[2] + ctm[4];
725
matrix.y0 = ctm[3] + ctm[5];
726
cairo_matrix_invert (&matrix);
727
cairo_pattern_set_matrix (pattern, &matrix);
729
cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST);
730
/* FIXME: Doesn't the image mask support any colorspace? */
731
cairo_set_source_rgb (cairo, fill_color.r, fill_color.g, fill_color.b);
732
cairo_mask (cairo, pattern);
734
cairo_pattern_destroy (pattern);
735
cairo_surface_destroy (image);
742
//TODO: lots more work here.
743
void ArthurOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
744
int width, int height,
745
GfxImageColorMap *colorMap,
746
GBool interpolate, int *maskColors, GBool inlineImg)
748
unsigned char *buffer;
756
int is_identity_transform;
758
buffer = (unsigned char *)gmallocn3(width, height, 4);
760
/* TODO: Do we want to cache these? */
761
imgStr = new ImageStream(str, width,
762
colorMap->getNumPixelComps(),
763
colorMap->getBits());
766
/* ICCBased color space doesn't do any color correction
767
* so check its underlying color space as well */
768
is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
769
(colorMap->getColorSpace()->getMode() == csICCBased &&
770
((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
773
for (y = 0; y < height; y++) {
774
dest = (unsigned int *) (buffer + y * 4 * width);
775
pix = imgStr->getLine();
776
colorMap->getRGBLine (pix, dest, width);
778
for (x = 0; x < width; x++) {
779
for (i = 0; i < colorMap->getNumPixelComps(); ++i) {
781
if (pix[i] < maskColors[2*i] * 255||
782
pix[i] > maskColors[2*i+1] * 255) {
783
*dest = *dest | 0xff000000;
787
pix += colorMap->getNumPixelComps();
792
m_image = new QImage(buffer, width, height, QImage::Format_ARGB32);
795
for (y = 0; y < height; y++) {
796
dest = (unsigned int *) (buffer + y * 4 * width);
797
pix = imgStr->getLine();
798
colorMap->getRGBLine (pix, dest, width);
801
m_image = new QImage(buffer, width, height, QImage::Format_RGB32);
804
if (m_image == NULL || m_image->isNull()) {
805
qDebug() << "Null image";
809
ctm = state->getCTM();
810
matrix.setMatrix(ctm[0] / width, ctm[1] / width, -ctm[2] / height, -ctm[3] / height, ctm[2] + ctm[4], ctm[3] + ctm[5]);
812
m_painter->setMatrix(matrix, true);
813
m_painter->drawImage( QPoint(0,0), *m_image );