diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index ae3df6f9ec..0a78913041 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -778,6 +778,15 @@ const char *QPdf::toHex(uchar u, char *buffer) return buffer; } +const char *QPdf::toHex(const QString &chars) +{ + QString out; + char buf[5]; + for (QChar ch : chars) + out.append(QPdf::toHex(ch.unicode(), buf)); + return out.toAscii(); +} + QPdfPage::QPdfPage() : QPdf::ByteStream(true) // Enable file backing @@ -2964,42 +2973,63 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) ? "0 .3 -1 0 0 Tm\n" : "0 0 -1 0 0 Tm\n"); - -#if 0 // #### implement actual text for complex languages const unsigned short *logClusters = ti.logClusters; int pos = 0; + int k = 0; + QHash charsHash; + QVector actualText(glyphs.size()); + QVector actualTextEnd(glyphs.size(), 0); do { int end = pos + 1; while (end < ti.num_chars && logClusters[end] == logClusters[pos]) ++end; - *currentPage << "/Span << /ActualText = QPdfEngine::Version_1_6 && ge - gs > 1) { // use ActualText for n:m, n:1 case. + // glyphs is made from ti.glyphs by deleting dontPrint char, and adding Kashida char. + int j = gs; + for(; j < ge - 1 && ti.glyphs.attributes[j].dontPrint; j++); + for(; k < glyphs.size() && glyphs[k] != ti.glyphs.glyphs[j]; k++); + if (k < glyphs.size()) + actualText[k] = charsFragment; + for (;j < ge - 1 && k < glyphs.size(); j++, k++){ + for(; j < ge - 1 && ti.glyphs.attributes[j].dontPrint; j++); + for(; k < glyphs.size() && glyphs[k] != ti.glyphs.glyphs[j]; k++); + } + if (k < glyphs.size()) + actualTextEnd[k] = 1; + for(j = gs; j < ge; j++) // for fail-safe, also use reverseMap(). + charsHash[j].append(""); + }else if (ge - gs == 1) { // use ToUnicode for 1:1, 1:m case. + if (!charsHash.contains(keyGlyph) || QString::compare(charsHash[keyGlyph], charsFragment)) + charsHash[keyGlyph].append(charsFragment); + }else{ // use reverseMap() for n:m, n:1 case. + for(int j = gs; j < ge; j++) + charsHash[j].append(""); } - *currentPage << "> >>\n" - "BDC\n" - "<"; - int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end]; - for (int gs = logClusters[pos]; gs < ge; ++gs) - *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf); - *currentPage << "> Tj\n" - "EMC\n"; pos = end; } while (pos < ti.num_chars); -#else + qreal last_x = 0.; qreal last_y = 0.; for (int i = 0; i < glyphs.size(); ++i) { + // an ActualText entry with a Span tag is supported above PDF 1.5 + if (pdfVersion >= QPdfEngine::Version_1_6 && actualText[i].size()) + *currentPage << "/Span << /ActualText >>\nBDC\n"; qreal x = positions[i].x.toReal(); qreal y = positions[i].y.toReal(); if (synthesized & QFontEngine::SynthesizedItalic) x += .3*y; x /= stretch; char buf[5]; - int g = font->addGlyph(glyphs[i]); + int g = font->addGlyph(glyphs[i], charsHash[(ushort)glyphs[i]]); *currentPage << x - last_x << last_y - y << "Td <" << QPdf::toHex((ushort)g, buf) << "> Tj\n"; + if (pdfVersion >= QPdfEngine::Version_1_6 && actualTextEnd[i] == 1) + *currentPage << "EMC\n"; last_x = x; last_y = y; } @@ -3017,7 +3047,7 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) x += .3*y; x /= stretch; char buf[5]; - int g = font->addGlyph(glyphs[i]); + int g = font->addGlyph(glyphs[i], charsHash[(ushort)glyphs[i]]); *currentPage << x - last_x << last_y - y << "Td <" << QPdf::toHex((ushort)g, buf) << "> Tj\n"; last_x = x; @@ -3025,7 +3055,6 @@ void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) } *currentPage << "EMC\n"; } -#endif *currentPage << "ET\n"; } diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h index e2526de67d..77d6cc2e3b 100644 --- a/src/gui/painting/qpdf_p.h +++ b/src/gui/painting/qpdf_p.h @@ -139,6 +139,7 @@ namespace QPdf { const char *toHex(ushort u, char *buffer); const char *toHex(uchar u, char *buffer); + const char *toHex(const QString &chars); } diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp index fb12b681a4..a26816e936 100644 --- a/src/gui/text/qfontsubset.cpp +++ b/src/gui/text/qfontsubset.cpp @@ -228,22 +228,21 @@ QByteArray QFontSubset::createToUnicodeMap() const QByteArray ranges = "<0000> <0000> <0000>\n"; QPdf::ByteStream s(&ranges); + Q_ASSERT(glyph_to_chars.size() == glyph_indices.size()); char buf[5]; for (int g = 1; g < nGlyphs(); ) { - int uc0 = reverseMap.at(g); - if (!uc0) { - ++g; - continue; - } + QString uc0 = glyph_to_chars.at(g); int start = g; int startLinear = 0; ++g; while (g < nGlyphs()) { - int uc = reverseMap[g]; + QString uc = glyph_to_chars.at(g); // cmaps can't have the high byte changing within one range, so we need to break on that as well - if (!uc || (g>>8) != (start >> 8)) + if ((g>>8) != (start >> 8)) break; - if (uc == uc0 + 1) { + if (uc.size() == 1 && uc0.size() == 1 && uc[0].unicode() == uc0[0].unicode() + 1) { +// According to the spec., following case is also permited. but ignore it. +// uc.size() >= 2 && uc.size() == uc0.size() && uc[0].unicode() == uc0[0].unicode() + 1 && uc.chopped(1) == uc0.chopped(1) if (!startLinear) startLinear = g - 1; } else { @@ -262,27 +261,29 @@ QByteArray QFontSubset::createToUnicodeMap() const if (endnonlinear > start) { s << '<' << QPdf::toHex((ushort)start, buf) << "> <"; s << QPdf::toHex((ushort)(endnonlinear - 1), buf) << "> "; - if (endnonlinear == start + 1) { - s << '<' << QPdf::toHex((ushort)reverseMap[start], buf) << ">\n"; - } else { + if (endnonlinear > start + 1) s << '['; - for (int i = start; i < endnonlinear; ++i) { + for (int i = start; i < endnonlinear; ++i) { + if (glyph_to_chars.at(i).size() != 0) + s << '<' << QPdf::toHex(glyph_to_chars.at(i).left(512)) << "> "; + else // for automatically inserted Kashida char or case of "n-glyphs:m-chars" s << '<' << QPdf::toHex((ushort)reverseMap[i], buf) << "> "; - } - s << "]\n"; } + if (endnonlinear > start + 1) + s << "]"; + s << "\n"; checkRanges(ts, ranges, nranges); } if (startLinear) { while (startLinear < g) { int len = g - startLinear; - int uc_start = reverseMap[startLinear]; + int uc_start = glyph_to_chars.at(startLinear)[0].unicode(); int uc_end = uc_start + len - 1; if ((uc_end >> 8) != (uc_start >> 8)) len = 256 - (uc_start & 0xff); s << '<' << QPdf::toHex((ushort)startLinear, buf) << "> <"; s << QPdf::toHex((ushort)(startLinear + len - 1), buf) << "> "; - s << '<' << QPdf::toHex((ushort)reverseMap[startLinear], buf) << ">\n"; + s << '<' << QPdf::toHex((ushort)uc_start, buf) << ">\n"; checkRanges(ts, ranges, nranges); startLinear += len; } @@ -300,11 +301,17 @@ QByteArray QFontSubset::createToUnicodeMap() const return touc; } -int QFontSubset::addGlyph(int index) +int QFontSubset::addGlyph(int index, const QString &chars) { - int idx = glyph_indices.indexOf(index); - if (idx < 0) { - idx = glyph_indices.size(); + int idx; + if (glyph_hash.contains(QPair(index, chars))) { + idx = glyph_hash.value(QPair(index, chars)); + }else{ + idx = glyph_hash.size(); + if (idx == 65535) // reached maximum number of glyphs as a single font + return 0; // return "/.notdef" + glyph_hash.insert(QPair(index, chars), idx); + glyph_to_chars.append(chars); glyph_indices.append(index); } return idx; diff --git a/src/gui/text/qfontsubset_p.h b/src/gui/text/qfontsubset_p.h index e7c6053beb..bc464d2b45 100644 --- a/src/gui/text/qfontsubset_p.h +++ b/src/gui/text/qfontsubset_p.h @@ -64,7 +64,7 @@ public: { fontEngine->ref.ref(); #ifndef QT_NO_PDF - addGlyph(0); + addGlyph(0, QString()); #endif } ~QFontSubset() { @@ -81,7 +81,7 @@ public: static QByteArray glyphName(unsigned short unicode, bool symbol); - int addGlyph(int index); + int addGlyph(int index, const QString &chars); #endif const int object_id; bool noEmbed; @@ -92,6 +92,9 @@ public: int nGlyphs() const { return glyph_indices.size(); } mutable QFixed emSquare; mutable QVector widths; +private: + QHash, int> glyph_hash; + QVector glyph_to_chars; }; QT_END_NAMESPACE