aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pdf
diff options
context:
space:
mode:
authorGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-01-25 21:01:34 +0000
committerGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-01-25 21:01:34 +0000
commit2a22e10ab2946c5590cd2a258427ce3ccfca9bfa (patch)
tree1d96a3c68ca4bbe51935d5d2f56e46ccfd63f969 /src/pdf
parentc921843d85b4e79857103cdb614f76f123efa7f5 (diff)
Add Truetype and Type 1 font embedding support
Sorry this is such a large CL. It was very exploratory for me to make this work. - Add an interface to SkFontHost to retrieve font information and provide NULL implementations on all platforms except Linux. - Segment large Type 1 fonts into fonts with shared resources with 255 glyphs each. - Convert the various Type 1 formats to the form PDF wants. - Update font as we draw text instead of as part of the graphical state. - Remove built-in font support, we can't really use it. Other changes I can pull out to a separate CL if you like. - Add SkTScopedPtr class. - Fix double free of resources. - Fix bug in resource unique-ifying code. - Don't print anything for any empty clip path. - Fix copy paste error - MiterLimit. - Fix sign extension bug in SkPDFString - Fix FlateTest rename that was missed on a previous commit. Review URL: http://codereview.appspot.com/4082042 git-svn-id: http://skia.googlecode.com/svn/trunk@728 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/pdf')
-rw-r--r--src/pdf/SkPDFDevice.cpp155
-rw-r--r--src/pdf/SkPDFDocument.cpp24
-rw-r--r--src/pdf/SkPDFFont.cpp843
-rw-r--r--src/pdf/SkPDFGraphicState.cpp4
-rw-r--r--src/pdf/SkPDFTypes.cpp8
5 files changed, 869 insertions, 165 deletions
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index fec9a4f883..fd458e0ea9 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -139,6 +139,8 @@ SkPDFDevice::SkPDFDevice(int width, int height)
fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value.
fGraphicStack[0].fTextScaleX = SK_Scalar1;
fGraphicStack[0].fTextFill = SkPaint::kFill_Style;
+ fGraphicStack[0].fFont = NULL;
+ fGraphicStack[0].fGraphicState = NULL;
fGraphicStack[0].fClip.setRect(0,0, width, height);
fGraphicStack[0].fTransform.reset();
}
@@ -158,17 +160,19 @@ void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
pushGS();
SkPath clipPath;
- if (!region.getBoundaryPath(&clipPath))
- clipPath.moveTo(SkIntToScalar(-1), SkIntToScalar(-1));
- emitPath(clipPath);
-
- SkPath::FillType clipFill = clipPath.getFillType();
- NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
- NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
- if (clipFill == SkPath::kEvenOdd_FillType)
- fContent.append("W* n ");
- else
- fContent.append("W n ");
+ if (region.getBoundaryPath(&clipPath)) {
+ emitPath(clipPath);
+
+ SkPath::FillType clipFill = clipPath.getFillType();
+ NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType,
+ false);
+ NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType,
+ false);
+ if (clipFill == SkPath::kEvenOdd_FillType)
+ fContent.append("W* n ");
+ else
+ fContent.append("W n ");
+ }
fGraphicStack[fGraphicStackIndex].fClip = region;
}
@@ -298,12 +302,25 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
SkScalar x, SkScalar y, const SkPaint& paint) {
SkPaint textPaint = calculateTextPaint(paint);
updateGSFromPaint(textPaint, true);
- SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont.get();
- uint16_t glyphs[len];
- size_t glyphsLength;
- glyphsLength = font->textToPDFGlyphs(text, len, textPaint, glyphs, len);
- textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ // Make sure we have a glyph id encoding.
+ SkAutoFree glyphStorage;
+ uint16_t* glyphIDs;
+ size_t numGlyphs;
+ if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+ numGlyphs = paint.textToGlyphs(text, len, NULL);
+ glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
+ SK_MALLOC_TEMP | SK_MALLOC_THROW);
+ glyphStorage.set(glyphIDs);
+ paint.textToGlyphs(text, len, glyphIDs);
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ } else {
+ SkASSERT((len & 1) == 0);
+ numGlyphs = len / 2;
+ glyphIDs = (uint16_t*)text;
+ }
+ SkAutoFree encodedStorage(
+ sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
SkScalar width;
SkScalar* widthPtr = NULL;
@@ -311,13 +328,26 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
widthPtr = &width;
SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
- alignText(glyphCacheProc, textPaint, glyphs, glyphsLength, &x, &y,
- widthPtr);
+ alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr);
fContent.append("BT\n");
setTextTransform(x, y, textPaint.getTextSkewX());
- fContent.append(SkPDFString::formatString(glyphs, glyphsLength,
- font->multiByteGlyphs()));
- fContent.append(" Tj\nET\n");
+ size_t consumedGlyphCount = 0;
+ while (numGlyphs > consumedGlyphCount) {
+ updateFont(textPaint, glyphIDs[consumedGlyphCount]);
+ SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
+ size_t encodedLength = numGlyphs * 2;
+ consumedGlyphCount += font->glyphsToPDFFontEncoding(
+ glyphIDs + consumedGlyphCount, numGlyphs - consumedGlyphCount,
+ encodedStorage.get(), &encodedLength);
+ if (font->multiByteGlyphs())
+ encodedLength /= 2;
+ fContent.append(
+ SkPDFString::formatString((const uint16_t*)encodedStorage.get(),
+ encodedLength,
+ font->multiByteGlyphs()));
+ fContent.append(" Tj\n");
+ }
+ fContent.append("ET\n");
// Draw underline and/or strikethrough if the paint has them.
// drawPosText() and drawTextOnPath() don't draw underline or strikethrough
@@ -346,21 +376,42 @@ void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
SkPaint textPaint = calculateTextPaint(paint);
updateGSFromPaint(textPaint, true);
- SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont.get();
- uint16_t glyphs[len];
- size_t glyphsLength;
- glyphsLength = font->textToPDFGlyphs(text, len, textPaint, glyphs, len);
- textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ // Make sure we have a glyph id encoding.
+ SkAutoFree glyphStorage;
+ uint16_t* glyphIDs;
+ size_t numGlyphs;
+ if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+ numGlyphs = paint.textToGlyphs(text, len, NULL);
+ glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
+ SK_MALLOC_TEMP | SK_MALLOC_THROW);
+ glyphStorage.set(glyphIDs);
+ paint.textToGlyphs(text, len, glyphIDs);
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ } else {
+ SkASSERT((len & 1) == 0);
+ numGlyphs = len / 2;
+ glyphIDs = (uint16_t*)text;
+ }
SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
fContent.append("BT\n");
- for (size_t i = 0; i < glyphsLength; i++) {
+ updateFont(textPaint, glyphIDs[0]);
+ for (size_t i = 0; i < numGlyphs; i++) {
+ SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont;
+ uint16_t encodedValue;
+ size_t encodedLength = 2;
+ if (font->glyphsToPDFFontEncoding(glyphIDs + i, 1, &encodedValue,
+ &encodedLength) == 0) {
+ updateFont(textPaint, glyphIDs[i]);
+ i--;
+ continue;
+ }
SkScalar x = pos[i * scalarsPerPos];
SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
- alignText(glyphCacheProc, textPaint, glyphs + i, 1, &x, &y, NULL);
+ alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
setTextTransform(x, y, textPaint.getTextSkewX());
- fContent.append(SkPDFString::formatString(glyphs + i, 1,
+ fContent.append(SkPDFString::formatString(&encodedValue, 1,
font->multiByteGlyphs()));
fContent.append(" Tj\n");
}
@@ -539,7 +590,7 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
// newGraphicState has been canonicalized so we can directly compare
// pointers.
- if (fGraphicStack[fGraphicStackIndex].fGraphicState.get() !=
+ if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
newGraphicState.get()) {
int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
if (resourceIndex < 0) {
@@ -550,7 +601,7 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
fContent.append("/G");
fContent.appendS32(resourceIndex);
fContent.append(" gs\n");
- fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState;
+ fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
}
SkColor newColor = newPaint.getColor();
@@ -565,22 +616,6 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
}
if (forText) {
- uint32_t fontID = SkTypeface::UniqueID(newPaint.getTypeface());
- if (fGraphicStack[fGraphicStackIndex].fTextSize !=
- newPaint.getTextSize() ||
- fGraphicStack[fGraphicStackIndex].fFont.get() == NULL ||
- fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID) {
- int fontIndex = getFontResourceIndex(fontID);
- fContent.append("/F");
- fContent.appendS32(fontIndex);
- fContent.append(" ");
- fContent.appendScalar(newPaint.getTextSize());
- fContent.append(" Tf\n");
- fGraphicStack[fGraphicStackIndex].fTextSize =
- newPaint.getTextSize();
- fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
- }
-
if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
newPaint.getTextScaleX()) {
SkScalar scale = newPaint.getTextScaleX();
@@ -604,9 +639,27 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) {
}
}
-int SkPDFDevice::getFontResourceIndex(uint32_t fontID) {
- SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResouceByID(fontID);
- newFont->unref(); // getFontResourceByID and SkRefPtr both took a ref.
+void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
+ uint32_t fontID = SkTypeface::UniqueID(paint.getTypeface());
+ if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() ||
+ fGraphicStack[fGraphicStackIndex].fFont == NULL ||
+ fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID ||
+ !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) {
+ int fontIndex = getFontResourceIndex(fontID, glyphID);
+ fContent.append("/F");
+ fContent.appendS32(fontIndex);
+ fContent.append(" ");
+ fContent.appendScalar(paint.getTextSize());
+ fContent.append(" Tf\n");
+ fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize();
+ fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex];
+ }
+}
+
+
+int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) {
+ SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(fontID, glyphID);
+ newFont->unref(); // getFontResource and SkRefPtr both took a ref.
int resourceIndex = fFontResources.find(newFont.get());
if (resourceIndex < 0) {
resourceIndex = fFontResources.count();
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index 2b1e3f731b..77a9fdbb61 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,12 +27,9 @@ void addResourcesToCatalog(int firstIndex, bool firstPage,
for (int i = firstIndex; i < resourceList->count(); i++) {
int index = resourceList->find((*resourceList)[i]);
if (index != i) {
- // The resource lists themselves should already be unique, so the
- // first page resources shouldn't have any dups (assuming the first
- // page resources are handled first).
- SkASSERT(!firstPage);
(*resourceList)[i]->unref();
resourceList->removeShuffle(i);
+ i--;
} else {
catalog->addObject((*resourceList)[i], firstPage);
}
@@ -68,6 +65,23 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) {
SkRefPtr<SkPDFObjRef> pageTreeRootRef = new SkPDFObjRef(pageTreeRoot);
pageTreeRootRef->unref(); // SkRefPtr and new both took a reference.
fDocCatalog->insert("Pages", pageTreeRootRef.get());
+
+ /* TODO(vandebo) output intent
+ SkRefPtr<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
+ outputIntent->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFName> intentSubtype = new SkPDFName("GTS_PDFA1");
+ intentSubtype->unref(); // SkRefPtr and new both took a reference.
+ outputIntent->insert("S", intentSubtype.get());
+ SkRefPtr<SkPDFString> intentIdentifier = new SkPDFString("sRGB");
+ intentIdentifier->unref(); // SkRefPtr and new both took a reference.
+ outputIntent->insert("OutputConditionIdentifier",
+ intentIdentifier.get());
+ SkRefPtr<SkPDFArray> intentArray = new SkPDFArray;
+ intentArray->unref(); // SkRefPtr and new both took a reference.
+ intentArray->append(outputIntent.get());
+ fDocCatalog->insert("OutputIntent", intentArray.get());
+ */
+
bool first_page = true;
for (int i = 0; i < fPages.count(); i++) {
int resourceCount = fPageResources.count();
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 1bbc53af4e..e39530c685 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,30 +14,296 @@
* limitations under the License.
*/
+#include <ctype.h>
+
+#include "SkFontHost.h"
#include "SkPaint.h"
#include "SkPDFFont.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypefaceInfo.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
#include "SkUtils.h"
namespace {
-uint16_t unicharToWinAnsiGlyphID(SkUnichar uniChar) {
- // TODO(vandebo) Quick hack to get text working. Either finish the
- // implementation of this, or remove it and use the encoding built into
- // the real font.
- if (uniChar < ' ')
- return 0;
- if (uniChar < '~')
- return uniChar;
- return 0;
+bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType,
+ size_t* size) {
+ // PFB sections have a two or six bytes header. 0x80 and a one byte
+ // section type followed by a four byte section length. Type one is
+ // an ASCII section (includes a length), type two is a binary section
+ // (includes a length) and type three is an EOF marker with no length.
+ const uint8_t* buf = *src;
+ if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType)
+ return false;
+ if (buf[1] == 3)
+ return true;
+ if (*len < 6)
+ return false;
+
+ *size = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24);
+ size_t consumed = *size + 6;
+ if (consumed > *len)
+ return false;
+ *src = *src + consumed;
+ *len = *len - consumed;
+ return true;
+}
+
+bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen,
+ size_t* dataLen, size_t* trailerLen) {
+ const uint8_t* srcPtr = src;
+ size_t remaining = size;
+
+ return parsePFBSection(&srcPtr, &remaining, 1, headerLen) &&
+ parsePFBSection(&srcPtr, &remaining, 2, dataLen) &&
+ parsePFBSection(&srcPtr, &remaining, 1, trailerLen) &&
+ parsePFBSection(&srcPtr, &remaining, 3, NULL);
+}
+
+/* The sections of a PFA file are implicitly defined. The body starts
+ * after the line containing "eexec," and the trailer starts with 512
+ * literal 0's followed by "cleartomark" (plus arbitrary white space).
+ *
+ * This function assumes that src is NUL terminated, but the NUL
+ * termination is not included in size.
+ *
+ */
+bool parsePFA(const char* src, size_t size, size_t* headerLen,
+ size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) {
+ const char* end = src + size;
+
+ const char* dataPos = strstr(src, "eexec");
+ if (!dataPos)
+ return false;
+ dataPos += strlen("eexec");
+ while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') &&
+ dataPos < end)
+ dataPos++;
+ *headerLen = dataPos - src;
+
+ const char* trailerPos = strstr(dataPos, "cleartomark");
+ if (!trailerPos)
+ return false;
+ int zeroCount = 0;
+ for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) {
+ if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') {
+ continue;
+ } else if (*trailerPos == '0') {
+ zeroCount++;
+ } else {
+ return false;
+ }
+ }
+ if (zeroCount != 512)
+ return false;
+
+ *hexDataLen = trailerPos - src - *headerLen;
+ *trailerLen = size - *headerLen - *hexDataLen;
+
+ // Verify that the data section is hex encoded and count the bytes.
+ int nibbles = 0;
+ for (; dataPos < trailerPos; dataPos++) {
+ if (isspace(*dataPos))
+ continue;
+ if (!isxdigit(*dataPos))
+ return false;
+ nibbles++;
+ }
+ *dataLen = (nibbles + 1) / 2;
+
+ return true;
+}
+
+int8_t hexToBin(uint8_t c) {
+ if (!isxdigit(c))
+ return -1;
+ if (c <= '9') return c - '0';
+ if (c <= 'F') return c - 'A' + 10;
+ if (c <= 'f') return c - 'a' + 10;
+ return -1;
+}
+
+SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen,
+ size_t* dataLen, size_t* trailerLen) {
+ // srcStream may be backed by a file or a unseekable fd, so we may not be
+ // able to use skip(), rewind(), or getMemoryBase(). read()ing through
+ // the input only once is doable, but very ugly. Furthermore, it'd be nice
+ // if the data was NUL terminated so that we can use strstr() to search it.
+ // Make as few copies as possible given these constraints.
+ SkDynamicMemoryWStream dynamicStream;
+ SkRefPtr<SkMemoryStream> staticStream;
+ const uint8_t* src;
+ size_t srcLen;
+ if ((srcLen = srcStream->getLength()) > 0) {
+ staticStream = new SkMemoryStream(srcLen + 1);
+ staticStream->unref(); // new and SkRefPtr both took a ref.
+ src = (const uint8_t*)staticStream->getMemoryBase();
+ if (srcStream->getMemoryBase() != NULL) {
+ memcpy((void *)src, srcStream->getMemoryBase(), srcLen);
+ } else {
+ size_t read = 0;
+ while (read < srcLen) {
+ size_t got = srcStream->read((void *)staticStream->getAtPos(),
+ srcLen - read);
+ if (got == 0)
+ return NULL;
+ read += got;
+ staticStream->seek(read);
+ }
+ }
+ ((uint8_t *)src)[srcLen] = 0;
+ } else {
+ static const size_t bufSize = 4096;
+ uint8_t buf[bufSize];
+ size_t amount;
+ while ((amount = srcStream->read(buf, bufSize)) > 0)
+ dynamicStream.write(buf, amount);
+ amount = 0;
+ dynamicStream.write(&amount, 1); // NULL terminator.
+ // getStream makes another copy, but we couldn't do any better.
+ src = (const uint8_t*)dynamicStream.getStream();
+ srcLen = dynamicStream.getOffset() - 1;
+ }
+
+ if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) {
+ SkMemoryStream* result =
+ new SkMemoryStream(*headerLen + *dataLen + *trailerLen);
+ memcpy((char*)result->getAtPos(), src + 6, *headerLen);
+ result->seek(*headerLen);
+ memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6, *dataLen);
+ result->seek(*headerLen + *dataLen);
+ memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6 + *dataLen,
+ *trailerLen);
+ result->rewind();
+ return result;
+ }
+
+ // A PFA has to be converted for PDF.
+ size_t hexDataLen;
+ if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen,
+ trailerLen)) {
+ SkMemoryStream* result =
+ new SkMemoryStream(*headerLen + *dataLen + *trailerLen);
+ memcpy((char*)result->getAtPos(), src, *headerLen);
+ result->seek(*headerLen);
+
+ const uint8_t* hexData = src + *headerLen;
+ const uint8_t* trailer = hexData + hexDataLen;
+ size_t outputOffset = 0;
+ uint8_t dataByte;
+ bool highNibble = true;
+ for (; hexData < trailer; hexData++) {
+ char curNibble = hexToBin(*hexData);
+ if (curNibble < 0)
+ continue;
+ if (highNibble) {
+ dataByte = curNibble << 4;
+ highNibble = false;
+ } else {
+ dataByte |= curNibble;
+ highNibble = true;
+ ((char *)result->getAtPos())[outputOffset++] = dataByte;
+ }
+ }
+ if (!highNibble)
+ ((char *)result->getAtPos())[outputOffset++] = dataByte;
+ SkASSERT(outputOffset == *dataLen);
+ result->seek(*headerLen + outputOffset);
+
+ memcpy((char *)result->getAtPos(), src + *headerLen + hexDataLen,
+ *trailerLen);
+ result->rewind();
+ return result;
+ }
+
+ return NULL;
+}
+
+void appendWidth(const int& width, SkPDFArray* array) {
+ SkRefPtr<SkPDFInt> widthInt = new SkPDFInt(width);
+ widthInt->unref(); // SkRefPtr and new both took a reference.
+ array->append(widthInt.get());
+}
+
+void appendVerticalAdvance(const SkPDFTypefaceInfo::VerticalMetric& advance,
+ SkPDFArray* array) {
+ appendWidth(advance.fVerticalAdvance, array);
+ appendWidth(advance.fOriginXDisp, array);
+ appendWidth(advance.fOriginYDisp, array);
}
+template <typename Data>
+SkPDFArray* composeAdvanceData(
+ SkPDFTypefaceInfo::AdvanceMetric<Data>* advanceInfo,
+ void (*appendAdvance)(const Data& advance, SkPDFArray* array),
+ Data* defaultAdvance) {
+ SkPDFArray* result = new SkPDFArray();
+ for (; advanceInfo != NULL; advanceInfo = advanceInfo->fNext.get()) {
+ switch (advanceInfo->fType) {
+ case SkPDFTypefaceInfo::WidthRange::kDefault: {
+ SkASSERT(advanceInfo->fAdvance.count() == 1);
+ *defaultAdvance = advanceInfo->fAdvance[0];
+ break;
+ }
+ case SkPDFTypefaceInfo::WidthRange::kRange: {
+ SkRefPtr<SkPDFArray> advanceArray = new SkPDFArray();
+ advanceArray->unref(); // SkRefPtr and new both took a ref.
+ for (int j = 0; j < advanceInfo->fAdvance.count(); j++)
+ appendAdvance(advanceInfo->fAdvance[j], advanceArray.get());
+ SkRefPtr<SkPDFInt> rangeStart =
+ new SkPDFInt(advanceInfo->fStartId);
+ rangeStart->unref(); // SkRefPtr and new both took a reference.
+ result->append(rangeStart.get());
+ result->append(advanceArray.get());
+ break;
+ }
+ case SkPDFTypefaceInfo::WidthRange::kRun: {
+ SkASSERT(advanceInfo->fAdvance.count() == 1);
+ SkRefPtr<SkPDFInt> rangeStart =
+ new SkPDFInt(advanceInfo->fStartId);
+ rangeStart->unref(); // SkRefPtr and new both took a reference.
+ result->append(rangeStart.get());
+
+ SkRefPtr<SkPDFInt> rangeEnd = new SkPDFInt(advanceInfo->fEndId);
+ rangeEnd->unref(); // SkRefPtr and new both took a reference.
+ result->append(rangeEnd.get());
+
+ appendAdvance(advanceInfo->fAdvance[0], result);
+ break;
+ }
+ }
+ }
+ return result;
}
+} // namespace
+
+/* Font subset design: It would be nice to be able to subset fonts
+ * (particularly type 3 fonts), but it's a lot of work and not a priority.
+ *
+ * Resources are canonicalized and uniqueified by pointer so there has to be
+ * some additional state indicating which subset of the font is used. It
+ * must be maintained at the page granularity and then combined at the document
+ * granularity. a) change SkPDFFont to fill in its state on demand, kind of
+ * like SkPDFGraphicState. b) maintain a per font glyph usage class in each
+ * page/pdf device. c) in the document, retrieve the per font glyph usage
+ * from each page and combine it and ask for a resource with that subset.
+ */
+
SkPDFFont::~SkPDFFont() {
SkAutoMutexAcquire lock(canonicalFontsMutex());
- int index = find(fFontID);
- SkASSERT(index >= 0);
- canonicalFonts().removeShuffle(index);
+ int index;
+ if (find(fFontID, fFirstGlyphID, &index)) {
+ canonicalFonts().removeShuffle(index);
+#ifdef SK_DEBUG
+ SkASSERT(!fDescendant);
+ } else {
+ SkASSERT(fDescendant);
+#endif
+ }
+ fResources.unrefAll();
}
void SkPDFFont::getResources(SkTDArray<SkPDFObject*>* resourceList) {
@@ -53,75 +319,68 @@ uint32_t SkPDFFont::fontID() {
return fFontID;
}
+bool SkPDFFont::hasGlyph(uint16_t id) {
+ return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0;
+}
+
bool SkPDFFont::multiByteGlyphs() {
return fMultiByteGlyphs;
}
-// TODO(vandebo) This function goes or stays with unicharToWinAnsiGlyphID.
-int SkPDFFont::textToPDFGlyphs(const void* text, size_t byteLength,
- const SkPaint& paint, uint16_t glyphs[],
- size_t glyphsLength) const {
- // For a regular, non built-in font, just ask the paint.
- if (fResources.count() != 0) {
- SkASSERT(glyphsLength >= byteLength / 2);
- if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
- memcpy(glyphs, text, byteLength / 2 * 2);
- return byteLength / 2;
- } else {
- return paint.textToGlyphs(text, byteLength, glyphs);
- }
- }
+size_t SkPDFFont::glyphsToPDFFontEncoding(const uint16_t* glyphIDs,
+ size_t numGlyphs, void* encodedValues,
+ size_t* encodedLength) {
+ if (numGlyphs * 2 > *encodedLength)
+ numGlyphs = *encodedLength / 2;
- const char* data = (const char*)text;
- const char* stop = data + byteLength;
- const uint16_t* data16 = (const uint16_t*)data;
- const uint16_t* stop16 = (const uint16_t*)stop;
- uint16_t* gPtr = glyphs;
- uint16_t* gEnd = glyphs + glyphsLength;
-
- // For a built-in font (no resources), we may have to undo glyph encoding
- // before converting to the standard pdf encoding.
- switch(paint.getTextEncoding()) {
- case SkPaint::kUTF8_TextEncoding:
- while (data < stop && gPtr < gEnd)
- *gPtr++ = unicharToWinAnsiGlyphID(SkUTF8_NextUnichar(&data));
- SkASSERT(data >= stop);
- break;
- case SkPaint::kUTF16_TextEncoding:
- while (data16 < stop16 && gPtr < gEnd)
- *gPtr++ = unicharToWinAnsiGlyphID(SkUTF16_NextUnichar(&data16));
- SkASSERT(data16 >= stop16);
- break;
- case SkPaint::kGlyphID_TextEncoding:
- while (data16 < stop16 && gPtr < gEnd) {
- SkUnichar buf;
- paint.glyphsToUnichars(data16++, 1, &buf);
- *gPtr++ = unicharToWinAnsiGlyphID(buf);
+ // A font with multibyte glyphs will support all glyph IDs in a single font,
+ // shortcut if we can.
+ if (fMultiByteGlyphs) {
+ *encodedLength = numGlyphs * 2;
+ memcpy(encodedValues, glyphIDs, *encodedLength);
+ } else {
+ char* output = (char*) encodedValues;
+ for (size_t i = 0; i < numGlyphs; i++) {
+ if (glyphIDs[i] == 0) {
+ output[i] = 0;
+ continue;
}
- SkASSERT(data16 >= stop16);
- break;
- default:
- SkASSERT(!"Unknown text encoding");
- break;
+ if (glyphIDs[i] < fFirstGlyphID || glyphIDs[i] > fLastGlyphID) {
+ numGlyphs = i;
+ break;
+ }
+ output[i] = glyphIDs[i] - fFirstGlyphID + 1;
+ }
}
- return gPtr - glyphs;
+
+ return numGlyphs;
}
// static
-SkPDFFont* SkPDFFont::getFontResouceByID(uint32_t fontID) {
+SkPDFFont* SkPDFFont::getFontResource(uint32_t fontID, uint16_t glyphID) {
SkAutoMutexAcquire lock(canonicalFontsMutex());
- int index = find(fontID);
- if (index >= 0) {
+ int index;
+ if (find(fontID, glyphID, &index)) {
canonicalFonts()[index].fFont->ref();
return canonicalFonts()[index].fFont;
}
- // TODO(vandebo) Lookup and create the font. For now, just use the built-in
- // Helevtica.
- SkPDFFont* font = new SkPDFFont(fontID, false);
- font->populateBuiltinFont("Helvetica");
+ SkRefPtr<SkPDFTypefaceInfo> fontInfo;
+ SkPDFDict* fontDescriptor = NULL;
+ if (index >= 0) {
+ SkPDFFont* relatedFont = canonicalFonts()[index].fFont;
+ SkASSERT(relatedFont->fFontInfo.get());
+ fontInfo = relatedFont->fFontInfo;
+ fontDescriptor = relatedFont->fDescriptor.get();
+ } else {
+ fontInfo = SkFontHost::GetPDFTypefaceInfo(fontID);
+ fontInfo->unref(); // SkRefPtr and get info both took a reference.
+ }
- FontRec newEntry(font, fontID);
+ SkPDFFont* font = new SkPDFFont(fontInfo.get(), fontID, glyphID, false,
+ fontDescriptor);
+ FontRec newEntry(font, fontID, font->fFirstGlyphID);
+ index = canonicalFonts().count();
canonicalFonts().push(newEntry);
return font; // Return the reference new SkPDFFont() created.
}
@@ -141,59 +400,437 @@ SkMutex& SkPDFFont::canonicalFontsMutex() {
}
// static
-int SkPDFFont::find(uint32_t fontID) {
- FontRec search(NULL, fontID);
- return canonicalFonts().find(search);
-}
-
-SkPDFFont::SkPDFFont(uint32_t fontID, bool multiByteGlyphs)
- : fFontID(fontID),
- fMultiByteGlyphs(multiByteGlyphs) {
-}
-
-void SkPDFFont::populateBuiltinFont(const char fontName[]) {
- SkASSERT(strcmp(fontName, "Time-Roman") == 0 ||
- strcmp(fontName, "Time-Bold") == 0 ||
- strcmp(fontName, "Time-Italic") == 0 ||
- strcmp(fontName, "Time-BoldItalic") == 0 ||
- strcmp(fontName, "Helvetica") == 0 ||
- strcmp(fontName, "Helvetica-Bold") == 0 ||
- strcmp(fontName, "Helvetica-Oblique") == 0 ||
- strcmp(fontName, "Helvetica-BoldOblique") == 0 ||
- strcmp(fontName, "Courier") == 0 ||
- strcmp(fontName, "Courier-Bold") == 0 ||
- strcmp(fontName, "Courier-Oblique") == 0 ||
- strcmp(fontName, "Courier-BoldOblique") == 0 ||
- strcmp(fontName, "Symbol") == 0 ||
- strcmp(fontName, "ZapfDingbats") == 0);
-
- SkRefPtr<SkPDFName> type = new SkPDFName("Font");
- type->unref(); // SkRefPtr and new both took a reference.
- insert("Type", type.get());
+bool SkPDFFont::find(uint32_t fontID, uint16_t glyphID, int* index) {
+ // TODO(vandebo) optimize this, do only one search?
+ FontRec search(NULL, fontID, glyphID);
+ *index = canonicalFonts().find(search);
+ if (*index >= 0)
+ return true;
+ search.fGlyphID = 0;
+ *index = canonicalFonts().find(search);
+ return false;
+}
+
+SkPDFFont::SkPDFFont(class SkPDFTypefaceInfo* fontInfo, uint32_t fontID,
+ uint16_t glyphID, bool descendantFont,
+ SkPDFDict* fontDescriptor)
+ : SkPDFDict("Font"),
+ fFontID(fontID),
+#ifdef SK_DEBUG
+ fDescendant(descendantFont),
+#endif
+ fMultiByteGlyphs(false),
+ fFirstGlyphID(1),
+ fLastGlyphID(fontInfo->fLastGlyphID),
+ fFontInfo(fontInfo),
+ fDescriptor(fontDescriptor) {
+
+ if (fontInfo->fMultiMaster) {
+ populateType3Font();
+ } else {
+ switch (fontInfo->fType) {
+ case SkPDFTypefaceInfo::kType1CID_Font:
+ case SkPDFTypefaceInfo::kTrueType_Font:
+ if (descendantFont)
+ populateCIDFont();
+ else
+ populateType0Font();
+ break;
+ case SkPDFTypefaceInfo::kType1_Font: {
+ uint16_t firstGlyphID = glyphID - (glyphID - 1) % 255;
+ uint16_t lastGlyphID = firstGlyphID + 255 - 1;
+ if (lastGlyphID > fLastGlyphID)
+ lastGlyphID = fLastGlyphID;
+ if (populateType1Font(firstGlyphID, lastGlyphID))
+ break;
+ // else, fall through.
+ }
+ case SkPDFTypefaceInfo::kOther_Font:
+ case SkPDFTypefaceInfo::kNotEmbeddable_Font:
+ populateType3Font();
+ break;
+ case SkPDFTypefaceInfo::kCFF_Font:
+ SkASSERT(false); // Not supported yet.
+ }
+ }
+
+ // Type1 fonts may hold on to the font info, otherwise we are done with it.
+ if (fontInfo->fType != SkPDFTypefaceInfo::kType1_Font)
+ fFontInfo = NULL;
+}
+
+void SkPDFFont::populateType0Font() {
+ fMultiByteGlyphs = true;
+
+ SkRefPtr<SkPDFName> subType = new SkPDFName("Type0");
+ subType->unref(); // SkRefPtr and new both took a reference.
+ insert("Subtype", subType.get());
+
+ SkRefPtr<SkPDFName> baseFont = new SkPDFName(fFontInfo.get()->fFontName);
+ baseFont->unref(); // SkRefPtr and new both took a reference.
+ insert("BaseFont", baseFont.get());
+
+ SkRefPtr<SkPDFName> encoding = new SkPDFName("Identity-H");
+ encoding->unref(); // SkRefPtr and new both took a reference.
+ insert("Encoding", encoding.get());
+
+ // TODO(vandebo) add a ToUnicode mapping.
+
+ SkRefPtr<SkPDFFont> cidFont = new SkPDFFont(fFontInfo.get(),
+ fFontID, 1, true, NULL);
+ fResources.push(cidFont.get()); // 2 refs: SkRefPtr, new. Pass one.
+
+ SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
+ descendantFonts->unref(); // SkRefPtr and new took a reference.
+ SkRefPtr<SkPDFObjRef> cidFontRef = new SkPDFObjRef(cidFont.get());
+ cidFontRef->unref(); // SkRefPtr and new both took a reference.
+ descendantFonts->append(cidFontRef.get());
+ insert("DescendantFonts", descendantFonts.get());
+}
+
+void SkPDFFont::populateCIDFont() {
+ fMultiByteGlyphs = true;
+
+ SkRefPtr<SkPDFName> subType;
+ if (fFontInfo.get()->fType == SkPDFTypefaceInfo::kType1CID_Font)
+ subType = new SkPDFName("CIDFontType0");
+ else if (fFontInfo.get()->fType == SkPDFTypefaceInfo::kTrueType_Font)
+ subType = new SkPDFName("CIDFontType2");
+ else
+ SkASSERT(false);
+ subType->unref(); // SkRefPtr and new both took a reference.
+ insert("Subtype", subType.get());
+
+ SkRefPtr<SkPDFName> baseFont = new SkPDFName(fFontInfo.get()->fFontName);
+ baseFont->unref(); // SkRefPtr and new both took a reference.
+ insert("BaseFont", baseFont.get());
+
+ SkRefPtr<SkPDFDict> sysInfo = new SkPDFDict;
+ sysInfo->unref(); // SkRefPtr and new both took a reference.
+
+ SkRefPtr<SkPDFString> adobeString = new SkPDFString("Adobe");
+ adobeString->unref(); // SkRefPtr and new both took a reference.
+ sysInfo->insert("Registry", adobeString.get());
+
+ SkRefPtr<SkPDFString> identityString = new SkPDFString("Identity");
+ identityString->unref(); // SkRefPtr and new both took a reference.
+ sysInfo->insert("Ordering", identityString.get());
+
+ SkRefPtr<SkPDFInt> supplement = new SkPDFInt(0);
+ supplement->unref(); // SkRefPtr and new both took a reference.
+ sysInfo->insert("Supplement", supplement.get());
+
+ insert("CIDSystemInfo", sysInfo.get());
+
+ addFontDescriptor(0);
+
+ if (fFontInfo.get()->fGlyphWidths.get()) {
+ int defaultWidth = 0;
+ SkRefPtr<SkPDFArray> widths =
+ composeAdvanceData(fFontInfo.get()->fGlyphWidths.get(),
+ &appendWidth, &defaultWidth);
+ widths->unref(); // SkRefPtr and compose both took a reference.
+ if (widths->size())
+ insert("W", widths.get());
+ if (defaultWidth != 0) {
+ SkRefPtr<SkPDFInt> defaultWidthInt =
+ new SkPDFInt(defaultWidth);
+ // SkRefPtr and compose both took a reference.
+ defaultWidthInt->unref();
+ insert("DW", defaultWidthInt.get());
+ }
+ }
+ if (fFontInfo.get()->fVerticalMetrics.get()) {
+ struct SkPDFTypefaceInfo::VerticalMetric defaultAdvance;
+ defaultAdvance.fVerticalAdvance = 0;
+ defaultAdvance.fOriginXDisp = 0;
+ defaultAdvance.fOriginYDisp = 0;
+ SkRefPtr<SkPDFArray> advances =
+ composeAdvanceData(fFontInfo.get()->fVerticalMetrics.get(),
+ &appendVerticalAdvance, &defaultAdvance);
+ advances->unref(); // SkRefPtr and compose both took a ref.
+ if (advances->size())
+ insert("W2", advances.get());
+ if (defaultAdvance.fVerticalAdvance ||
+ defaultAdvance.fOriginXDisp ||
+ defaultAdvance.fOriginYDisp) {
+ SkRefPtr<SkPDFArray> defaultAdvanceArray = new SkPDFArray;
+ // SkRefPtr and compose both took a reference.
+ defaultAdvanceArray->unref();
+ appendVerticalAdvance(defaultAdvance,
+ defaultAdvanceArray.get());
+ insert("DW2", defaultAdvanceArray.get());
+ }
+ }
+}
+
+bool SkPDFFont::populateType1Font(uint16_t firstGlyphID, uint16_t lastGlyphID) {
+ SkASSERT(!fFontInfo.get()->fVerticalMetrics.get());
+ SkASSERT(fFontInfo.get()->fGlyphWidths.get());
+
+ int defaultWidth = 0;
+ const SkPDFTypefaceInfo::WidthRange* widthRangeEntry = NULL;
+ const SkPDFTypefaceInfo::WidthRange* widthEntry;
+ for (widthEntry = fFontInfo.get()->fGlyphWidths.get();
+ widthEntry != NULL;
+ widthEntry = widthEntry->fNext.get()) {
+ switch (widthEntry->fType) {
+ case SkPDFTypefaceInfo::WidthRange::kDefault:
+ defaultWidth = widthEntry->fAdvance[0];
+ break;
+ case SkPDFTypefaceInfo::WidthRange::kRun:
+ SkASSERT(false);
+ break;
+ case SkPDFTypefaceInfo::WidthRange::kRange:
+ SkASSERT(widthRangeEntry == NULL);
+ widthRangeEntry = widthEntry;
+ break;
+ }
+ }
+
+ if (!addFontDescriptor(defaultWidth))
+ return false;
+
+ fFirstGlyphID = firstGlyphID;
+ fLastGlyphID = lastGlyphID;
SkRefPtr<SkPDFName> subType = new SkPDFName("Type1");
subType->unref(); // SkRefPtr and new both took a reference.
insert("Subtype", subType.get());
- SkRefPtr<SkPDFName> baseFont = new SkPDFName(fontName);
+ SkRefPtr<SkPDFName> baseFont = new SkPDFName(fFontInfo.get()->fFontName);
baseFont->unref(); // SkRefPtr and new both took a reference.
insert("BaseFont", baseFont.get());
- SkRefPtr<SkPDFName> encoding = new SkPDFName("WinAnsiEncoding");
+ SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
+ widthArray->unref(); // SkRefPtr and new both took a ref.
+ int firstChar = 0;
+ if (widthRangeEntry) {
+ int startIndex = firstGlyphID - widthRangeEntry->fStartId;
+ int endIndex = startIndex + lastGlyphID - firstGlyphID + 1;
+ if (startIndex < 0)
+ startIndex = 0;
+ if (endIndex > widthRangeEntry->fAdvance.count())
+ endIndex = widthRangeEntry->fAdvance.count();
+ if (widthRangeEntry->fStartId == 0) {
+ appendWidth(widthRangeEntry->fAdvance[0], widthArray.get());
+ } else {
+ firstChar = startIndex + widthRangeEntry->fStartId;
+ }
+ for (int i = startIndex; i < endIndex; i++)
+ appendWidth(widthRangeEntry->fAdvance[i], widthArray.get());
+ } else {
+ appendWidth(defaultWidth, widthArray.get());
+ }
+ insert("Widths", widthArray.get());
+
+ SkRefPtr<SkPDFInt> firstCharInt = new SkPDFInt(firstChar);
+ firstCharInt->unref(); // SkRefPtr and new both took a reference.
+ insert("FirstChar", firstCharInt.get());
+
+ SkRefPtr<SkPDFInt> lastChar =
+ new SkPDFInt(firstChar + widthArray->size() - 1);
+ lastChar->unref(); // SkRefPtr and new both took a reference.
+ insert("LastChar", lastChar.get());
+
+ SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
encoding->unref(); // SkRefPtr and new both took a reference.
insert("Encoding", encoding.get());
+
+ SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
+ encDiffs->unref(); // SkRefPtr and new both took a reference.
+ encoding->insert("Differences", encDiffs.get());
+
+ encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2);
+ SkRefPtr<SkPDFInt> startID = new SkPDFInt(1);
+ startID->unref(); // SkRefPtr and new both took a reference.
+ encDiffs->append(startID.get());
+ for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) {
+ SkRefPtr<SkPDFName> glyphName =
+ new SkPDFName(fFontInfo.get()->fGlyphNames->get()[gID]);
+ glyphName->unref(); // SkRefPtr and new both took a reference.
+ encDiffs->append(glyphName.get());
+ }
+
+ if (fFontInfo.get()->fLastGlyphID <= 255)
+ fFontInfo = NULL;
+ return true;
}
-void SkPDFFont::populateFont(const char subType[], const char fontName[],
- int firstChar, int lastChar, int widths[],
- SkPDFObject* fontDescriptor) {
+void SkPDFFont::populateType3Font() {
+ // TODO(vandebo)
+ SkASSERT(false);
}
+bool SkPDFFont::addFontDescriptor(int defaultWidth) {
+ if (fDescriptor.get() != NULL) {
+ fResources.push(fDescriptor.get());
+ fDescriptor->ref();
+ SkRefPtr<SkPDFObjRef> descRef = new SkPDFObjRef(fDescriptor.get());
+ descRef->unref(); // SkRefPtr and new both took a reference.
+ insert("FontDescriptor", descRef.get());
+ return true;
+ }
+
+ fDescriptor = new SkPDFDict("FontDescriptor");
+ fDescriptor->unref(); // SkRefPtr and new both took a ref.
+
+ switch (fFontInfo.get()->fType) {
+ case SkPDFTypefaceInfo::kType1_Font: {
+ size_t header, data, trailer;
+ SkRefPtr<SkStream> rawFontData =
+ SkFontHost::OpenStream(fFontID);
+ rawFontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkStream* fontData = handleType1Stream(rawFontData.get(), &header,
+ &data, &trailer);
+ if (fontData == NULL)
+ return false;
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData);
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ SkRefPtr<SkPDFInt> headerLen = new SkPDFInt(header);
+ headerLen->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length1", headerLen.get());
+ SkRefPtr<SkPDFInt> dataLen = new SkPDFInt(data);
+ dataLen->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length2", dataLen.get());
+ SkRefPtr<SkPDFInt> trailerLen = new SkPDFInt(trailer);
+ trailerLen->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length3", trailerLen.get());
+
+ SkRefPtr<SkPDFObjRef> streamRef = new SkPDFObjRef(fontStream.get());
+ streamRef->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("FontFile", streamRef.get());
+ break;
+ }
+ case SkPDFTypefaceInfo::kTrueType_Font: {
+ SkRefPtr<SkStream> fontData = SkFontHost::OpenStream(fFontID);
+ fontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ SkRefPtr<SkPDFInt> length = new SkPDFInt(fontData->getLength());
+ length->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length1", length.get());
+
+ SkRefPtr<SkPDFObjRef> streamRef = new SkPDFObjRef(fontStream.get());
+ streamRef->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("FontFile2", streamRef.get());
+ break;
+ }
+ case SkPDFTypefaceInfo::kCFF_Font:
+ case SkPDFTypefaceInfo::kType1CID_Font: {
+ SkRefPtr<SkStream> fontData = SkFontHost::OpenStream(fFontID);
+ fontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ SkRefPtr<SkPDFName> subtype;
+ if (fFontInfo.get()->fType == SkPDFTypefaceInfo::kCFF_Font)
+ subtype = new SkPDFName("Type1C");
+ else
+ subtype = new SkPDFName("CIDFontType0c");
+ subtype->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Subtype", subtype.get());
+
+ SkRefPtr<SkPDFObjRef> streamRef = new SkPDFObjRef(fontStream.get());
+ streamRef->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("FontFile3", streamRef.get());
+ break;
+ }
+ default:
+ SkASSERT(false);
+ }
+
+ fResources.push(fDescriptor.get());
+ fDescriptor->ref();
+ SkRefPtr<SkPDFObjRef> descRef = new SkPDFObjRef(fDescriptor.get());
+ descRef->unref(); // SkRefPtr and new both took a reference.
+ insert("FontDescriptor", descRef.get());
+
+ SkRefPtr<SkPDFName> fontName =
+ new SkPDFName(fFontInfo.get()->fFontName);
+ fontName->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("FontName", fontName.get());
+
+ SkRefPtr<SkPDFInt> flags = new SkPDFInt(fFontInfo.get()->fStyle);
+ flags->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("Flags", flags.get());
+
+ SkIRect glyphBBox = fFontInfo.get()->fBBox;
+ SkRefPtr<SkPDFArray> bbox = new SkPDFArray;
+ bbox->unref(); // SkRefPtr and new both took a reference.
+ bbox->reserve(4);
+ SkRefPtr<SkPDFInt> bboxXMin = new SkPDFInt(glyphBBox.fLeft);
+ bboxXMin->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxXMin.get());
+ SkRefPtr<SkPDFInt> bboxYMin = new SkPDFInt(glyphBBox.fBottom);
+ bboxYMin->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxYMin.get());
+ SkRefPtr<SkPDFInt> bboxXMax = new SkPDFInt(glyphBBox.fRight);
+ bboxXMax->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxXMax.get());
+ SkRefPtr<SkPDFInt> bboxYMax = new SkPDFInt(glyphBBox.fTop);
+ bboxYMax->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxYMax.get());
+ fDescriptor->insert("FontBBox", bbox.get());
+
+ SkRefPtr<SkPDFInt> italicAngle =
+ new SkPDFInt(fFontInfo.get()->fItalicAngle);
+ italicAngle->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("ItalicAngle", italicAngle.get());
+
+ SkRefPtr<SkPDFScalar> ascent = new SkPDFScalar(fFontInfo.get()->fAscent);
+ ascent->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("Ascent", ascent.get());
+
+ SkRefPtr<SkPDFScalar> descent = new SkPDFScalar(fFontInfo.get()->fDescent);
+ descent->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("Descent", descent.get());
+
+ SkRefPtr<SkPDFScalar> capHeight =
+ new SkPDFScalar(fFontInfo.get()->fCapHeight);
+ capHeight->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("CapHeight", capHeight.get());
+
+ SkRefPtr<SkPDFScalar> stemV = new SkPDFScalar(fFontInfo.get()->fStemV);
+ stemV->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("StemV", stemV.get());
+
+ if (defaultWidth > 0) {
+ SkRefPtr<SkPDFInt> defaultWidthInt = new SkPDFInt(defaultWidth);
+ defaultWidthInt->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("MissingWidth", defaultWidthInt.get());
+ }
+ return true;
+}
+
+
bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const {
- return fFontID == b.fFontID;
+ if (fFontID != b.fFontID)
+ return false;
+ if (fFont != NULL && b.fFont != NULL) {
+ return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID &&
+ fFont->fLastGlyphID == b.fFont->fLastGlyphID;
+ }
+ if (fGlyphID == 0 || b.fGlyphID == 0)
+ return true;
+
+ if (fFont != NULL) {
+ return fFont->fFirstGlyphID <= b.fGlyphID &&
+ b.fGlyphID <= fFont->fLastGlyphID;
+ } else if (b.fFont != NULL) {
+ return b.fFont->fFirstGlyphID <= fGlyphID &&
+ fGlyphID <= b.fFont->fLastGlyphID;
+ }
+ return fGlyphID == b.fGlyphID;
}
-SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID)
+SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID)
: fFont(font),
- fFontID(fontID) {
+ fFontID(fontID),
+ fGlyphID(glyphID) {
}
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 1fb62fa199..286468bb62 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -114,7 +114,7 @@ void SkPDFGraphicState::populateDict() {
SkRefPtr<SkPDFScalar> strokeMiterLimit = new SkPDFScalar(
fPaint.getStrokeMiter());
strokeMiterLimit->unref(); // SkRefPtr and new both took a reference.
- insert("ML", strokeWidth.get());
+ insert("ML", strokeMiterLimit.get());
// Turn on automatic stroke adjustment.
SkRefPtr<SkPDFBool> trueVal = new SkPDFBool(true);
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 9649f0baec..506b86d37f 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,7 +97,7 @@ void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
}
SkPDFString::SkPDFString(const char value[])
- : fValue(formatString(value, sizeof(value))) {
+ : fValue(formatString(value, strlen(value))) {
}
SkPDFString::SkPDFString(const SkString& value)
@@ -156,7 +156,7 @@ SkString SkPDFString::doFormatString(const void* input, size_t len,
for (size_t i = 0; i < len; i++) {
SkASSERT(!wideInput || !(win[i] & ~0xFF));
char val = wideInput ? win[i] : cin[i];
- if (val & 0x80 || val < ' ') {
+ if (val > '~' || val < ' ') {
sevenBitClean = false;
break;
}
@@ -177,7 +177,7 @@ SkString SkPDFString::doFormatString(const void* input, size_t len,
result.append("<");
for (size_t i = 0; i < len; i++) {
SkASSERT(!wideInput || !(win[i] & ~0xFF));
- char val = wideInput ? win[i] : cin[i];
+ unsigned char val = wideInput ? win[i] : cin[i];
result.appendHex(val, 2);
}
result.append(">");