* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/pdf/SkPDFMakeToUnicodeCmap.h"
#include "include/private/SkTo.h"
#include "src/pdf/SkPDFUtils.h"
#include "src/utils/SkUTF.h"
static void append_tounicode_header(SkDynamicMemoryWStream* cmap,
bool multibyte) {
const char* kHeader =
"/CIDInit /ProcSet findresource begin\n"
"12 dict begin\n"
"begincmap\n";
cmap->writeText(kHeader);
const char* kSysInfo =
"/CIDSystemInfo\n"
"<< /Registry (Adobe)\n"
"/Ordering (UCS)\n"
"/Supplement 0\n"
">> def\n";
cmap->writeText(kSysInfo);
const char* kTypeInfoHeader =
"/CMapName /Adobe-Identity-UCS def\n"
"/CMapType 2 def\n"
"1 begincodespacerange\n";
cmap->writeText(kTypeInfoHeader);
if (multibyte) {
cmap->writeText("<0000> <FFFF>\n");
} else {
cmap->writeText("<00> <FF>\n");
}
cmap->writeText("endcodespacerange\n");
}
static void append_cmap_footer(SkDynamicMemoryWStream* cmap) {
const char kFooter[] =
"endcmap\n"
"CMapName currentdict /CMap defineresource pop\n"
"end\n"
"end";
cmap->writeText(kFooter);
}
namespace {
struct BFChar {
SkGlyphID fGlyphId;
SkUnichar fUnicode;
};
struct BFRange {
SkGlyphID fStart;
SkGlyphID fEnd;
SkUnichar fUnicode;
};
}
static void write_glyph(SkDynamicMemoryWStream* cmap,
bool multiByte,
SkGlyphID gid) {
if (multiByte) {
SkPDFUtils::WriteUInt16BE(cmap, gid);
} else {
SkPDFUtils::WriteUInt8(cmap, SkToU8(gid));
}
}
static void append_bfchar_section(const std::vector<BFChar>& bfchar,
bool multiByte,
SkDynamicMemoryWStream* cmap) {
for (size_t i = 0; i < bfchar.size(); i += 100) {
int count = SkToInt(bfchar.size() - i);
count = SkMin32(count, 100);
cmap->writeDecAsText(count);
cmap->writeText(" beginbfchar\n");
for (int j = 0; j < count; ++j) {
cmap->writeText("<");
write_glyph(cmap, multiByte, bfchar[i + j].fGlyphId);
cmap->writeText("> <");
SkPDFUtils::WriteUTF16beHex(cmap, bfchar[i + j].fUnicode);
cmap->writeText(">\n");
}
cmap->writeText("endbfchar\n");
}
}
static void append_bfrange_section(const std::vector<BFRange>& bfrange,
bool multiByte,
SkDynamicMemoryWStream* cmap) {
for (size_t i = 0; i < bfrange.size(); i += 100) {
int count = SkToInt(bfrange.size() - i);
count = SkMin32(count, 100);
cmap->writeDecAsText(count);
cmap->writeText(" beginbfrange\n");
for (int j = 0; j < count; ++j) {
cmap->writeText("<");
write_glyph(cmap, multiByte, bfrange[i + j].fStart);
cmap->writeText("> <");
write_glyph(cmap, multiByte, bfrange[i + j].fEnd);
cmap->writeText("> <");
SkPDFUtils::WriteUTF16beHex(cmap, bfrange[i + j].fUnicode);
cmap->writeText(">\n");
}
cmap->writeText("endbfrange\n");
}
}
void SkPDFAppendCmapSections(const SkUnichar* glyphToUnicode,
const SkPDFGlyphUse* subset,
SkDynamicMemoryWStream* cmap,
bool multiByteGlyphs,
SkGlyphID firstGlyphID,
SkGlyphID lastGlyphID) {
int glyphOffset = 0;
if (!multiByteGlyphs) {
glyphOffset = firstGlyphID - 1;
}
std::vector<BFChar> bfcharEntries;
std::vector<BFRange> bfrangeEntries;
BFRange currentRangeEntry = {0, 0, 0};
bool rangeEmpty = true;
const int limit = (int)lastGlyphID + 1 - glyphOffset;
for (int i = firstGlyphID - glyphOffset; i < limit + 1; ++i) {
SkGlyphID gid = i + glyphOffset;
bool inSubset = i < limit && (subset == nullptr || subset->has(gid));
if (!rangeEmpty) {
bool inRange =
i == currentRangeEntry.fEnd + 1 &&
i >> 8 == currentRangeEntry.fStart >> 8 &&
i < limit &&
glyphToUnicode[gid] ==
currentRangeEntry.fUnicode + i - currentRangeEntry.fStart;
if (!inSubset || !inRange) {
if (currentRangeEntry.fEnd > currentRangeEntry.fStart) {
bfrangeEntries.push_back(currentRangeEntry);
} else {
bfcharEntries.push_back({currentRangeEntry.fStart, currentRangeEntry.fUnicode});
}
rangeEmpty = true;
}
}
if (inSubset) {
currentRangeEntry.fEnd = i;
if (rangeEmpty) {
currentRangeEntry.fStart = i;
currentRangeEntry.fUnicode = glyphToUnicode[gid];
rangeEmpty = false;
}
}
}
append_bfchar_section(bfcharEntries, multiByteGlyphs, cmap);
append_bfrange_section(bfrangeEntries, multiByteGlyphs, cmap);
}
std::unique_ptr<SkStreamAsset> SkPDFMakeToUnicodeCmap(
const SkUnichar* glyphToUnicode,
const SkPDFGlyphUse* subset,
bool multiByteGlyphs,
SkGlyphID firstGlyphID,
SkGlyphID lastGlyphID) {
SkDynamicMemoryWStream cmap;
append_tounicode_header(&cmap, multiByteGlyphs);
SkPDFAppendCmapSections(glyphToUnicode, subset, &cmap, multiByteGlyphs,
firstGlyphID, lastGlyphID);
append_cmap_footer(&cmap);
return cmap.detachAsStream();
}