svx/source/inc/svdpdf.hxx | 2 svx/source/svdraw/svdpdf.cxx | 139 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 123 insertions(+), 18 deletions(-)
New commits: commit e10deac930b7aa982f2ab921d57975d4028314db Author: Caolán McNamara <[email protected]> AuthorDate: Tue Sep 30 11:55:33 2025 +0100 Commit: Caolán McNamara <[email protected]> CommitDate: Wed Oct 15 12:02:55 2025 +0200 handle cid ranges, minimal pass through for now example has multiple subsets fonts all with the same cid ranges, but no actualy glyphs in them, so just pass through the declared ranges for now. Change-Id: Ie38cba1b44c1cae5e3e87576561e52a061bf6266 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191679 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> (cherry picked from commit c610e1b81ecdabc3bfb9ef9d369e323891791c66) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192355 Reviewed-by: Caolán McNamara <[email protected]> Tested-by: Jenkins diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx index c506f096b2c1..bb97c4b5ebf5 100644 --- a/svx/source/inc/svdpdf.hxx +++ b/svx/source/inc/svdpdf.hxx @@ -59,6 +59,8 @@ struct FontSubSet // What glyphs are in the subset and what characters those represent. std::map<sal_Int32, OString> glyphToChars; std::map<OString, sal_Int32> charsToGlyph; + // What glyphs ranges are in the subset + std::map<sal_Int32, Range> glyphRangesToChars; int nGlyphCount; }; diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx index cb8923a1e36a..8a460791c085 100644 --- a/svx/source/svdraw/svdpdf.cxx +++ b/svx/source/svdraw/svdpdf.cxx @@ -1186,6 +1186,14 @@ static bool toPfaCID(SubSetInfo& rSubSetInfo, const OUString& fileUrl, return true; } +static void appendFourByteHex(OStringBuffer& rBuffer, sal_uInt32 nNumber) +{ + OString sCodePoint = OString::number(nNumber, 16); + for (sal_Int32 j = sCodePoint.getLength(); j < 4; ++j) + rBuffer.append('0'); + rBuffer.append(sCodePoint); +} + static OString decomposeLegacyUnicodeLigature(UChar32 cUnicode) { UErrorCode eCode(U_ZERO_ERROR); @@ -1206,12 +1214,7 @@ static OString decomposeLegacyUnicodeLigature(UChar32 cUnicode) { OStringBuffer aBuffer; for (int32_t i = 0, nLen = sDecomposed.length(); i < nLen; ++i) - { - OString sCodePoint = OString::number(sDecomposed[i], 16); - for (sal_Int32 j = sCodePoint.getLength(); j < 4; ++j) - aBuffer.append('0'); - aBuffer.append(sCodePoint); - } + appendFourByteHex(aBuffer, sDecomposed[i]); return aBuffer.toString(); } } @@ -1256,22 +1259,30 @@ static void buildCMapAndFeatures(const OUString& CMapUrl, const OUString& Featur SvMemoryStream aInCMap(const_cast<uint8_t*>(toUnicodeData.data()), toUnicodeData.size(), StreamMode::READ); - OString sLine; - while (aInCMap.ReadLine(sLine)) - { - if (sLine.endsWith("beginbfchar")) - break; - } std::vector<OString> bfcharlines; + std::vector<OString> bfcharranges; - if (sLine.endsWith("beginbfchar")) + OString sLine; + while (aInCMap.ReadLine(sLine)) { - while (aInCMap.ReadLine(sLine)) + if (sLine.endsWith("beginbfchar")) { - if (sLine.endsWith("endbfchar")) - break; - bfcharlines.push_back(sLine); + while (aInCMap.ReadLine(sLine)) + { + if (sLine.endsWith("endbfchar")) + break; + bfcharlines.push_back(sLine); + } + } + else if (sLine.endsWith("beginbfrange")) + { + while (aInCMap.ReadLine(sLine)) + { + if (sLine.endsWith("endbfrange")) + break; + bfcharranges.push_back(sLine); + } } } @@ -1282,6 +1293,51 @@ static void buildCMapAndFeatures(const OUString& CMapUrl, const OUString& Featur std::map<sal_Int32, OString> ligatureGlyphToChars; std::vector<sal_Int32> glyphs; + if (!bfcharranges.empty()) + { + assert(!bNameKeyed); + OString beginline = OString::number(bfcharranges.size()) + " begincidrange"; + CMap.WriteLine(beginline); + for (const auto& charrange : bfcharranges) + { + assert(charrange[0] == '<'); + sal_Int32 nEnd = charrange.indexOf('>', 1); + assert(charrange[nEnd] == '>'); + sal_Int32 nGlyphRangeStart = o3tl::toInt32(charrange.subView(1, nEnd - 1), 16); + + OString remainder(o3tl::trim(charrange.subView(nEnd + 1))); + assert(remainder[0] == '<'); + nEnd = remainder.indexOf('>', 1); + assert(remainder[nEnd] == '>'); + sal_Int32 nGlyphRangeEnd = o3tl::toInt32(remainder.subView(1, nEnd - 1), 16); + + sal_Int32 nGlyphRangeLen = nGlyphRangeEnd - nGlyphRangeStart; + + assert(nGlyphRangeLen > 0); + + OString sChars(o3tl::trim(remainder.subView(nEnd + 1))); + assert(sChars[0] == '<' && sChars[sChars.getLength() - 1] == '>'); + OString sContents = sChars.copy(1, sChars.getLength() - 2); + //TODO, it might be that there are cases of ligatures here too in + //which case I presume that it can only be with a range of a single + //glyph(?). If that's how it works, then we could push the entry + //instead into bfcharlines. + assert(sContents.getLength() == 4); + sal_Int32 nCharRangeStart = o3tl::toInt32(sContents, 16); + OStringBuffer aBuffer("<"); + appendFourByteHex(aBuffer, nCharRangeStart); + aBuffer.append("> <"); + sal_Int32 nCharRangeEnd = nCharRangeStart + nGlyphRangeLen; + appendFourByteHex(aBuffer, nCharRangeEnd); + aBuffer.append("> "_ostr + OString::number(nGlyphRangeStart)); + OString cidrangeline = aBuffer.toString(); + rSubSetInfo.aComponents.back().glyphRangesToChars[nGlyphRangeStart] + = Range(nCharRangeStart, nCharRangeEnd); + CMap.WriteLine(cidrangeline); + } + CMap.WriteLine("endcidrange"); + } + if (!bfcharlines.empty()) { OString beginline = OString::number(bfcharlines.size()) + " begincidchar"; @@ -1427,13 +1483,60 @@ static EmbeddedFontInfo mergeFontSubsets(const OUString& mergedFontUrl, mergedCMap.WriteBytes(cmapprefix, std::size(cmapprefix) - 1); - sal_Int32 cidcharcount(0); + sal_Int32 cidcharcount(0), cidrangecount(0); + bool differentcidranges(false); for (const auto& component : rSubSetInfo.aComponents) cidcharcount += component.glyphToChars.size(); + for (size_t i = 0, size = rSubSetInfo.aComponents.size(); i < size; ++i) + { + const auto& component = rSubSetInfo.aComponents[i]; + if (i == 0) + { + cidrangecount += component.glyphRangesToChars.size(); + continue; + } + + if (component.glyphRangesToChars != rSubSetInfo.aComponents[i - 1].glyphRangesToChars) + { + cidrangecount += component.glyphRangesToChars.size(); + differentcidranges = true; + } + } + + assert(!differentcidranges && "TODO: deal with this when an example arises"); + if (differentcidranges) + return EmbeddedFontInfo(); + std::map<sal_Int32, OString> ligatureGlyphToChars; std::map<OString, sal_Int32> charsToGlyph; + if (cidrangecount) + { + OString beginline = OString::number(cidrangecount) + " begincidrange"; + mergedCMap.WriteLine(beginline); + + const auto& component = rSubSetInfo.aComponents[0]; + for (const auto& entry : component.glyphRangesToChars) + { + sal_Int32 glyphrangebegin = entry.first; + sal_Int32 charrangebegin = entry.second.Min(); + sal_Int32 charrangeend = entry.second.Max(); + + OStringBuffer aBuffer("<"); + appendFourByteHex(aBuffer, charrangebegin); + aBuffer.append("> <"); + appendFourByteHex(aBuffer, charrangeend); + aBuffer.append("> "_ostr + OString::number(glyphrangebegin)); + OString cidrangeline = aBuffer.toString(); + + mergedCMap.WriteLine(cidrangeline); + } + + // glyphoffset += component.nGlyphCount; + mergedCMap.WriteLine("endcidrange"); + } + if (cidcharcount) { sal_Int32 glyphoffset(0);
