int CALLBACK GDIFontInfo::EnumerateFontsForFamily( const ENUMLOGFONTEXW *lpelfe, const NEWTEXTMETRICEXW *nmetrics, DWORD fontType, LPARAM data) { EnumerateFontsForFamilyData *famData = reinterpret_cast<EnumerateFontsForFamilyData*>(data); HDC hdc = famData->mFontInfo.mHdc; LOGFONTW logFont = lpelfe->elfLogFont; const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; AutoSelectFont font(hdc, &logFont); if (!font.IsValid()) { return 1; } FontFaceData fontData; nsDependentString fontName(lpelfe->elfFullName); // callback called for each style-charset so return if style already seen if (fontName.Equals(famData->mPreviousFontName)) { return 1; } famData->mPreviousFontName = fontName; famData->mFontInfo.mLoadStats.fonts++; // read name table info bool nameDataLoaded = false; if (famData->mFontInfo.mLoadFaceNames || famData->mFontInfo.mLoadOtherNames) { uint32_t kNAME = NativeEndian::swapToBigEndian(TRUETYPE_TAG('n','a','m','e')); uint32_t nameSize; AutoFallibleTArray<uint8_t, 1024> nameData; nameSize = ::GetFontData(hdc, kNAME, 0, nullptr, 0); if (nameSize != GDI_ERROR && nameSize > 0 && nameData.SetLength(nameSize, fallible)) { ::GetFontData(hdc, kNAME, 0, nameData.Elements(), nameSize); // face names if (famData->mFontInfo.mLoadFaceNames) { gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize, gfxFontUtils::NAME_ID_FULL, fontData.mFullName); gfxFontUtils::ReadCanonicalName((const char*)(nameData.Elements()), nameSize, gfxFontUtils::NAME_ID_POSTSCRIPT, fontData.mPostscriptName); nameDataLoaded = true; famData->mFontInfo.mLoadStats.facenames++; } // other family names if (famData->mFontInfo.mLoadOtherNames) { gfxFontFamily::ReadOtherFamilyNamesForFace(famData->mFamilyName, (const char*)(nameData.Elements()), nameSize, famData->mOtherFamilyNames, false); } } } // read cmap bool cmapLoaded = false; gfxWindowsFontType feType = GDIFontEntry::DetermineFontType(metrics, fontType); if (famData->mFontInfo.mLoadCmaps && (feType == GFX_FONT_TYPE_PS_OPENTYPE || feType == GFX_FONT_TYPE_TT_OPENTYPE || feType == GFX_FONT_TYPE_TRUETYPE)) { uint32_t kCMAP = NativeEndian::swapToBigEndian(TRUETYPE_TAG('c','m','a','p')); uint32_t cmapSize; AutoFallibleTArray<uint8_t, 1024> cmapData; cmapSize = ::GetFontData(hdc, kCMAP, 0, nullptr, 0); if (cmapSize != GDI_ERROR && cmapSize > 0 && cmapData.SetLength(cmapSize, fallible)) { ::GetFontData(hdc, kCMAP, 0, cmapData.Elements(), cmapSize); bool cmapLoaded = false; bool unicodeFont = false, symbolFont = false; RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(); uint32_t offset; if (NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData.Elements(), cmapSize, *charmap, offset, unicodeFont, symbolFont))) { fontData.mCharacterMap = charmap; fontData.mUVSOffset = offset; fontData.mSymbolFont = symbolFont; cmapLoaded = true; famData->mFontInfo.mLoadStats.cmaps++; } } } if (cmapLoaded || nameDataLoaded) { famData->mFontInfo.mFontFaceData.Put(fontName, fontData); } return 1; }
nsresult gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext, gfxShapedText *aShapedText, uint32_t aOffset, uint32_t aLength, const char16_t *aText, gr_segment *aSegment) { int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit(); bool rtl = aShapedText->IsRightToLeft(); uint32_t glyphCount = gr_seg_n_slots(aSegment); // identify clusters; graphite may have reordered/expanded/ligated glyphs. AutoFallibleTArray<Cluster,SMALL_GLYPH_RUN> clusters; AutoFallibleTArray<uint16_t,SMALL_GLYPH_RUN> gids; AutoFallibleTArray<float,SMALL_GLYPH_RUN> xLocs; AutoFallibleTArray<float,SMALL_GLYPH_RUN> yLocs; if (!clusters.SetLength(aLength, fallible) || !gids.SetLength(glyphCount, fallible) || !xLocs.SetLength(glyphCount, fallible) || !yLocs.SetLength(glyphCount, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } // walk through the glyph slots and check which original character // each is associated with uint32_t gIndex = 0; // glyph slot index uint32_t cIndex = 0; // current cluster index for (const gr_slot *slot = gr_seg_first_slot(aSegment); slot != nullptr; slot = gr_slot_next_in_segment(slot), gIndex++) { uint32_t before = gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot))); uint32_t after = gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot))); gids[gIndex] = gr_slot_gid(slot); xLocs[gIndex] = gr_slot_origin_X(slot); yLocs[gIndex] = gr_slot_origin_Y(slot); // if this glyph has a "before" character index that precedes the // current cluster's char index, we need to merge preceding // clusters until it gets included while (before < clusters[cIndex].baseChar && cIndex > 0) { clusters[cIndex-1].nChars += clusters[cIndex].nChars; clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs; --cIndex; } // if there's a gap between the current cluster's base character and // this glyph's, extend the cluster to include the intervening chars if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars && before >= clusters[cIndex].baseChar + clusters[cIndex].nChars) { NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word"); Cluster& c = clusters[cIndex + 1]; c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars; c.nChars = before - c.baseChar; c.baseGlyph = gIndex; c.nGlyphs = 0; ++cIndex; } // increment cluster's glyph count to include current slot NS_ASSERTION(cIndex < aLength, "cIndex beyond word length"); ++clusters[cIndex].nGlyphs; // extend cluster if necessary to reach the glyph's "after" index if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) { clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar; } } bool roundX; bool roundY; aContext->GetRoundOffsetsToPixels(&roundX, &roundY); gfxShapedText::CompressedGlyph *charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset; // now put glyphs into the textrun, one cluster at a time for (uint32_t i = 0; i <= cIndex; ++i) { const Cluster& c = clusters[i]; float adv; // total advance of the cluster if (rtl) { if (i == 0) { adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph]; } else { adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph]; } } else { if (i == cIndex) { adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph]; } else { adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph]; } } // Check for default-ignorable char that didn't get filtered, combined, // etc by the shaping process, and skip it. uint32_t offs = c.baseChar; NS_ASSERTION(offs < aLength, "unexpected offset"); if (c.nGlyphs == 1 && c.nChars == 1 && aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) { continue; } uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits : NSToIntRound(adv * dev2appUnits); if (c.nGlyphs == 1 && gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) && gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) && charGlyphs[offs].IsClusterStart() && yLocs[c.baseGlyph] == 0) { charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]); } else { // not a one-to-one mapping with simple metrics: use DetailedGlyph nsAutoTArray<gfxShapedText::DetailedGlyph,8> details; float clusterLoc; for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) { gfxShapedText::DetailedGlyph* d = details.AppendElement(); d->mGlyphID = gids[j]; d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits : -yLocs[j] * dev2appUnits; if (j == c.baseGlyph) { d->mXOffset = 0; d->mAdvance = appAdvance; clusterLoc = xLocs[j]; } else { float dx = rtl ? (xLocs[j] - clusterLoc) : (xLocs[j] - clusterLoc - adv); d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits : dx * dev2appUnits; d->mAdvance = 0; } } gfxShapedText::CompressedGlyph g; g.SetComplex(charGlyphs[offs].IsClusterStart(), true, details.Length()); aShapedText->SetGlyphs(aOffset + offs, g, details.Elements()); } for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) { NS_ASSERTION(j < aLength, "unexpected offset"); gfxShapedText::CompressedGlyph &g = charGlyphs[j]; NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph"); g.SetComplex(g.IsClusterStart(), false, 0); } } return NS_OK; }
nsresult gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText, uint32_t aOffset, uint32_t aLength, CTRunRef aCTRun, int32_t aStringOffset) { // The word has been bidi-wrapped; aStringOffset is the number // of chars at the beginning of the CTLine that we should skip. // aCTRun is a glyph run from the CoreText layout process. int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1; int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun); if (numGlyphs == 0) { return NS_OK; } int32_t wordLength = aLength; // character offsets get really confusing here, as we have to keep track of // (a) the text in the actual textRun we're constructing // (c) the string that was handed to CoreText, which contains the text of the font run // plus directional-override padding // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line // (but may extend beyond the actual font run into the bidi wrapping text). // aStringOffset tells us how many initial characters of the line to ignore. // get the source string range within the CTLine's text CFRange stringRange = ::CTRunGetStringRange(aCTRun); // skip the run if it is entirely outside the actual range of the font run if (stringRange.location - aStringOffset + stringRange.length <= 0 || stringRange.location - aStringOffset >= wordLength) { return NS_OK; } // retrieve the laid-out glyph data from the CTRun UniquePtr<CGGlyph[]> glyphsArray; UniquePtr<CGPoint[]> positionsArray; UniquePtr<CFIndex[]> glyphToCharArray; const CGGlyph* glyphs = nullptr; const CGPoint* positions = nullptr; const CFIndex* glyphToChar = nullptr; // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds, // and so allocating a new array and copying data with CTRunGetGlyphs // will be extremely rare. // If this were not the case, we could use an nsAutoTArray<> to // try and avoid the heap allocation for small runs. // It's possible that some future change to CoreText will mean that // CTRunGetGlyphsPtr fails more often; if this happens, nsAutoTArray<> // may become an attractive option. glyphs = ::CTRunGetGlyphsPtr(aCTRun); if (!glyphs) { glyphsArray = MakeUniqueFallible<CGGlyph[]>(numGlyphs); if (!glyphsArray) { return NS_ERROR_OUT_OF_MEMORY; } ::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get()); glyphs = glyphsArray.get(); } positions = ::CTRunGetPositionsPtr(aCTRun); if (!positions) { positionsArray = MakeUniqueFallible<CGPoint[]>(numGlyphs); if (!positionsArray) { return NS_ERROR_OUT_OF_MEMORY; } ::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get()); positions = positionsArray.get(); } // Remember that the glyphToChar indices relate to the CoreText line, // not to the beginning of the textRun, the font run, // or the stringRange of the glyph run glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun); if (!glyphToChar) { glyphToCharArray = MakeUniqueFallible<CFIndex[]>(numGlyphs); if (!glyphToCharArray) { return NS_ERROR_OUT_OF_MEMORY; } ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get()); glyphToChar = glyphToCharArray.get(); } double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0), nullptr, nullptr, nullptr); nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs; gfxShapedText::CompressedGlyph *charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset; // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph // to a source text character; we also need the charindex-to-glyphindex mapping to // find the glyph for a given char. Note that some chars may not map to any glyph // (ligature continuations), and some may map to several glyphs (eg Indic split vowels). // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we // record the last glyph index for cases where the char maps to several glyphs, // so that our clumping will include all the glyph fragments for the character. // The charToGlyph array is indexed by char position within the stringRange of the glyph run. static const int32_t NO_GLYPH = -1; AutoFallibleTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray; if (!charToGlyphArray.SetLength(stringRange.length, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } int32_t *charToGlyph = charToGlyphArray.Elements(); for (int32_t offset = 0; offset < stringRange.length; ++offset) { charToGlyph[offset] = NO_GLYPH; } for (int32_t i = 0; i < numGlyphs; ++i) { int32_t loc = glyphToChar[i] - stringRange.location; if (loc >= 0 && loc < stringRange.length) { charToGlyph[loc] = i; } } // Find character and glyph clumps that correspond, allowing for ligatures, // indic reordering, split glyphs, etc. // // The idea is that we'll find a character sequence starting at the first char of stringRange, // and extend it until it includes the character associated with the first glyph; // we also extend it as long as there are "holes" in the range of glyphs. So we // will eventually have a contiguous sequence of characters, starting at the beginning // of the range, that map to a contiguous sequence of glyphs, starting at the beginning // of the glyph array. That's a clump; then we update the starting positions and repeat. // // NB: In the case of RTL layouts, we iterate over the stringRange in reverse. // // This may find characters that fall outside the range 0:wordLength, // so we won't necessarily use everything we find here. bool isRightToLeft = aShapedText->IsRightToLeft(); int32_t glyphStart = 0; // looking for a clump that starts at this glyph index int32_t charStart = isRightToLeft ? stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run) while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for bool inOrder = true; int32_t charEnd = glyphToChar[glyphStart] - stringRange.location; NS_WARN_IF_FALSE(charEnd >= 0 && charEnd < stringRange.length, "glyph-to-char mapping points outside string range"); // clamp charEnd to the valid range of the string charEnd = std::max(charEnd, 0); charEnd = std::min(charEnd, int32_t(stringRange.length)); int32_t glyphEnd = glyphStart; int32_t charLimit = isRightToLeft ? -1 : stringRange.length; do { // This is normally executed once for each iteration of the outer loop, // but in unusual cases where the character/glyph association is complex, // the initial character range might correspond to a non-contiguous // glyph range with "holes" in it. If so, we will repeat this loop to // extend the character range until we have a contiguous glyph sequence. NS_ASSERTION((direction > 0 && charEnd < charLimit) || (direction < 0 && charEnd > charLimit), "no characters left in range?"); charEnd += direction; while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) { charEnd += direction; } // find the maximum glyph index covered by the clump so far if (isRightToLeft) { for (int32_t i = charStart; i > charEnd; --i) { if (charToGlyph[i] != NO_GLYPH) { // update extent of glyph range glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1); } } } else { for (int32_t i = charStart; i < charEnd; ++i) { if (charToGlyph[i] != NO_GLYPH) { // update extent of glyph range glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1); } } } if (glyphEnd == glyphStart + 1) { // for the common case of a single-glyph clump, we can skip the following checks break; } if (glyphEnd == glyphStart) { // no glyphs, try to extend the clump continue; } // check whether all glyphs in the range are associated with the characters // in our clump; if not, we have a discontinuous range, and should extend it // unless we've reached the end of the text bool allGlyphsAreWithinCluster = true; int32_t prevGlyphCharIndex = charStart; for (int32_t i = glyphStart; i < glyphEnd; ++i) { int32_t glyphCharIndex = glyphToChar[i] - stringRange.location; if (isRightToLeft) { if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) { allGlyphsAreWithinCluster = false; break; } if (glyphCharIndex > prevGlyphCharIndex) { inOrder = false; } prevGlyphCharIndex = glyphCharIndex; } else { if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) { allGlyphsAreWithinCluster = false; break; } if (glyphCharIndex < prevGlyphCharIndex) { inOrder = false; } prevGlyphCharIndex = glyphCharIndex; } } if (allGlyphsAreWithinCluster) { break; } } while (charEnd != charLimit); NS_WARN_IF_FALSE(glyphStart < glyphEnd, "character/glyph clump contains no glyphs!"); if (glyphStart == glyphEnd) { ++glyphStart; // make progress - avoid potential infinite loop charStart = charEnd; continue; } NS_WARN_IF_FALSE(charStart != charEnd, "character/glyph clump contains no characters!"); if (charStart == charEnd) { glyphStart = glyphEnd; // this is bad - we'll discard the glyph(s), // as there's nowhere to attach them continue; } // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd; // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature), // and endCharIndex to the limit (position beyond the last char), // adjusting for the offset of the stringRange relative to the textRun. int32_t baseCharIndex, endCharIndex; if (isRightToLeft) { while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) { charEnd--; } baseCharIndex = charEnd + stringRange.location - aStringOffset + 1; endCharIndex = charStart + stringRange.location - aStringOffset + 1; } else { while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) { charEnd++; } baseCharIndex = charStart + stringRange.location - aStringOffset; endCharIndex = charEnd + stringRange.location - aStringOffset; } // Then we check if the clump falls outside our actual string range; if so, just go to the next. if (endCharIndex <= 0 || baseCharIndex >= wordLength) { glyphStart = glyphEnd; charStart = charEnd; continue; } // Ensure we won't try to go beyond the valid length of the word's text baseCharIndex = std::max(baseCharIndex, 0); endCharIndex = std::min(endCharIndex, wordLength); // Now we're ready to set the glyph info in the textRun; measure the glyph width // of the first (perhaps only) glyph, to see if it is "Simple" int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit(); double toNextGlyph; if (glyphStart < numGlyphs-1) { toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x; } else { toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x; } int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit); // Check if it's a simple one-to-one mapping int32_t glyphsInClump = glyphEnd - glyphStart; if (glyphsInClump == 1 && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && charGlyphs[baseCharIndex].IsClusterStart() && positions[glyphStart].y == 0.0) { charGlyphs[baseCharIndex].SetSimpleGlyph(advance, glyphs[glyphStart]); } else { // collect all glyphs in a list to be assigned to the first char; // there must be at least one in the clump, and we already measured its advance, // hence the placement of the loop-exit test and the measurement of the next glyph while (1) { gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement(); details->mGlyphID = glyphs[glyphStart]; details->mXOffset = 0; details->mYOffset = -positions[glyphStart].y * appUnitsPerDevUnit; details->mAdvance = advance; if (++glyphStart >= glyphEnd) { break; } if (glyphStart < numGlyphs-1) { toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x; } else { toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x; } advance = int32_t(toNextGlyph * appUnitsPerDevUnit); } gfxTextRun::CompressedGlyph textRunGlyph; textRunGlyph.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(), true, detailedGlyphs.Length()); aShapedText->SetGlyphs(aOffset + baseCharIndex, textRunGlyph, detailedGlyphs.Elements()); detailedGlyphs.Clear(); } // the rest of the chars in the group are ligature continuations, no associated glyphs while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) { gfxShapedText::CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex]; NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph"); shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0); } glyphStart = glyphEnd; charStart = charEnd; } return NS_OK; }
bool PluginScriptableObjectParent::AnswerInvokeDefault(const InfallibleTArray<Variant>& aArgs, Variant* aResult, bool* aSuccess) { if (!mObject) { NS_WARNING("Calling AnswerInvoke with an invalidated object!"); *aResult = void_t(); *aSuccess = false; return true; } NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); NS_ASSERTION(mType == LocalObject, "Bad type!"); PluginInstanceParent* instance = GetInstance(); if (!instance) { NS_ERROR("No instance?!"); *aResult = void_t(); *aSuccess = false; return true; } const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); if (!npn) { NS_ERROR("No netscape funcs?!"); *aResult = void_t(); *aSuccess = false; return true; } AutoFallibleTArray<NPVariant, 10> convertedArgs; uint32_t argCount = aArgs.Length(); if (!convertedArgs.SetLength(argCount)) { *aResult = void_t(); *aSuccess = false; return true; } for (uint32_t index = 0; index < argCount; index++) { if (!ConvertToVariant(aArgs[index], convertedArgs[index], instance)) { // Don't leak things we've already converted! while (index-- > 0) { ReleaseVariant(convertedArgs[index], instance); } *aResult = void_t(); *aSuccess = false; return true; } } NPVariant result; bool success = npn->invokeDefault(instance->GetNPP(), mObject, convertedArgs.Elements(), argCount, &result); for (uint32_t index = 0; index < argCount; index++) { ReleaseVariant(convertedArgs[index], instance); } if (!success) { *aResult = void_t(); *aSuccess = false; return true; } Variant convertedResult; success = ConvertToRemoteVariant(result, convertedResult, GetInstance()); DeferNPVariantLastRelease(npn, &result); if (!success) { *aResult = void_t(); *aSuccess = false; return true; } *aResult = convertedResult; *aSuccess = true; return true; }