void RenderNamedFlowFragment::computeChildrenStyleInRegion(RenderElement& renderer) { for (auto& child : childrenOfType<RenderObject>(renderer)) { auto it = m_renderObjectRegionStyle.find(&child); RefPtr<RenderStyle> childStyleInRegion; bool objectRegionStyleCached = false; if (it != m_renderObjectRegionStyle.end()) { childStyleInRegion = it->value.style; objectRegionStyleCached = true; } else { if (child.isAnonymous() || child.isInFlowRenderFlowThread()) childStyleInRegion = RenderStyle::createAnonymousStyleWithDisplay(&renderer.style(), child.style().display()); else if (child.isText()) childStyleInRegion = RenderStyle::clone(&renderer.style()); else childStyleInRegion = computeStyleInRegion(toRenderElement(child), renderer.style()); } setObjectStyleInRegion(&child, childStyleInRegion, objectRegionStyleCached); if (child.isRenderElement()) computeChildrenStyleInRegion(toRenderElement(child)); } }
void InlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/) { RenderElement& renderer = toRenderElement(this->renderer()); if (!paintInfo.shouldPaintWithinRoot(renderer) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) return; LayoutPoint childPoint = paintOffset; if (parent()->renderer().style()->isFlippedBlocksWritingMode() && renderer.isBox()) // Faster than calling containingBlock(). childPoint = m_renderer.containingBlock()->flipForWritingModeForChild(&toRenderBox(renderer), childPoint); // Paint all phases of replaced elements atomically, as though the replaced element established its // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 // specification.) bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; PaintInfo info(paintInfo); info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; renderer.paint(info, childPoint); if (!preservePhase) { info.phase = PaintPhaseChildBlockBackgrounds; renderer.paint(info, childPoint); info.phase = PaintPhaseFloat; renderer.paint(info, childPoint); info.phase = PaintPhaseForeground; renderer.paint(info, childPoint); info.phase = PaintPhaseOutline; renderer.paint(info, childPoint); } }
void RenderNamedFlowFragment::restoreRegionObjectsOriginalStyle() { if (!hasCustomRegionStyle()) return; RenderObjectRegionStyleMap temp; for (auto& objectPair : m_renderObjectRegionStyle) { RenderObject* object = const_cast<RenderObject*>(objectPair.key); RefPtr<RenderStyle> objectRegionStyle = &object->style(); RefPtr<RenderStyle> objectOriginalStyle = objectPair.value.style; if (object->isRenderElement()) toRenderElement(object)->setStyleInternal(*objectOriginalStyle); bool shouldCacheRegionStyle = objectPair.value.cached; if (!shouldCacheRegionStyle) { // Check whether we should cache the computed style in region. unsigned changedContextSensitiveProperties = ContextSensitivePropertyNone; StyleDifference styleDiff = objectOriginalStyle->diff(objectRegionStyle.get(), changedContextSensitiveProperties); if (styleDiff < StyleDifferenceLayoutPositionedMovementOnly) shouldCacheRegionStyle = true; } if (shouldCacheRegionStyle) { ObjectRegionStyleInfo styleInfo; styleInfo.style = objectRegionStyle; styleInfo.cached = true; temp.set(object, styleInfo); } } m_renderObjectRegionStyle.swap(temp); }
static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter) { ASSERT(object); // Real text nodes don't have their own style so they can't have counters. // We can't even look at their styles or we'll see extra resets and increments! if (object->isText()) return nullptr; RenderElement* element = toRenderElement(object); if (element->hasCounterNodeMap()) { if (CounterMap* nodeMap = counterMaps().get(element)) { if (CounterNode* node = nodeMap->get(identifier)) return node; } } bool isReset = false; int value = 0; if (!planCounter(element, identifier, isReset, value) && !alwaysCreateCounter) return nullptr; RefPtr<CounterNode> newParent = 0; RefPtr<CounterNode> newPreviousSibling = 0; RefPtr<CounterNode> newNode = CounterNode::create(element, isReset, value); if (findPlaceForCounter(element, identifier, isReset, newParent, newPreviousSibling)) newParent->insertAfter(newNode.get(), newPreviousSibling.get(), identifier); CounterMap* nodeMap; if (element->hasCounterNodeMap()) nodeMap = counterMaps().get(element); else { nodeMap = new CounterMap; counterMaps().set(element, adoptPtr(nodeMap)); element->setHasCounterNodeMap(true); } nodeMap->set(identifier, newNode); if (newNode->parent()) return newNode.get(); // Checking if some nodes that were previously counter tree root nodes // should become children of this node now. CounterMaps& maps = counterMaps(); Element* stayWithin = parentOrPseudoHostElement(element); bool skipDescendants; for (RenderElement* currentRenderer = nextInPreOrder(element, stayWithin); currentRenderer; currentRenderer = nextInPreOrder(currentRenderer, stayWithin, skipDescendants)) { skipDescendants = false; if (!currentRenderer->hasCounterNodeMap()) continue; CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier); if (!currentCounter) continue; skipDescendants = true; if (currentCounter->parent()) continue; if (stayWithin == parentOrPseudoHostElement(currentRenderer) && currentCounter->hasResetType()) break; newNode->insertAfter(currentCounter, newNode->lastChild(), identifier); } return newNode.get(); }
static inline bool findPreviousAndNextAttributes(RenderElement* start, RenderSVGInlineText* locateElement, bool& stopAfterNext, SVGTextLayoutAttributes*& previous, SVGTextLayoutAttributes*& next) { ASSERT(start); ASSERT(locateElement); // FIXME: Make this iterative. for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { if (child->isSVGInlineText()) { RenderSVGInlineText* text = toRenderSVGInlineText(child); if (locateElement != text) { if (stopAfterNext) { next = text->layoutAttributes(); return true; } previous = text->layoutAttributes(); continue; } stopAfterNext = true; continue; } if (!child->isSVGInline()) continue; if (findPreviousAndNextAttributes(toRenderElement(child), locateElement, stopAfterNext, previous, next)) return true; } return false; }
void RenderNamedFlowFragment::computeChildrenStyleInRegion(const RenderElement* object) { for (RenderObject* child = object->firstChild(); child; child = child->nextSibling()) { auto it = m_renderObjectRegionStyle.find(child); RefPtr<RenderStyle> childStyleInRegion; bool objectRegionStyleCached = false; if (it != m_renderObjectRegionStyle.end()) { childStyleInRegion = it->value.style; objectRegionStyleCached = true; } else { if (child->isAnonymous() || child->isInFlowRenderFlowThread()) childStyleInRegion = RenderStyle::createAnonymousStyleWithDisplay(&object->style(), child->style().display()); else if (child->isText()) childStyleInRegion = RenderStyle::clone(&object->style()); else childStyleInRegion = computeStyleInRegion(child); } setObjectStyleInRegion(child, childStyleInRegion, objectRegionStyleCached); if (child->isRenderElement()) computeChildrenStyleInRegion(toRenderElement(child)); } }
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { auto activePaintingResource = this->activePaintingResource(); if (!activePaintingResource) { // TODO: We're only supporting simple filled HTML text so far. RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource(); solidPaintingResource->setColor(context->fillColor()); activePaintingResource = solidPaintingResource; } auto& elementRenderer = renderer().isRenderElement() ? toRenderElement(renderer()) : *renderer().parent(); RenderStyle& style = elementRenderer.style(); ASSERT(activePaintingResource); RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; auto translator(createGlyphToPathTranslator(*fontData, glyphBuffer, from, numGlyphs, point)); while (translator->containsMorePaths()) { Path glyphPath = translator->nextPath(); if (activePaintingResource->applyResource(elementRenderer, style, context, resourceMode)) { float strokeThickness = context->strokeThickness(); if (renderer().isSVGInlineText()) context->setStrokeThickness(strokeThickness * toRenderSVGInlineText(renderer()).scalingFactor()); activePaintingResource->postApplyResource(elementRenderer, context, resourceMode, &glyphPath, 0); context->setStrokeThickness(strokeThickness); } } }
void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject& renderer) { if (renderer.isAnonymous()) return; RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); if (!rendererCanHaveResources(renderer)) return; RenderElement& elementRenderer = toRenderElement(renderer); SVGResourcesCache* cache = resourcesCacheFromRenderObject(elementRenderer); cache->removeResourcesFromRenderer(elementRenderer); }
void RenderMathMLRoot::addChild(RenderObject* newChild, RenderObject* beforeChild) { // Insert an implicit <mrow> for <mroot> as well as <msqrt>, to ensure firstChild() will have a box // to measure and store a glyph-based height for preferredLogicalHeightAfterSizing. if (!firstChild()) RenderMathMLBlock::addChild(RenderMathMLRow::createAnonymousWithParentRenderer(this)); // An <mroot>'s index has { position: absolute }. if (newChild->style().position() == AbsolutePosition) RenderMathMLBlock::addChild(newChild); else toRenderElement(firstChild())->addChild(newChild, beforeChild && beforeChild->parent() == firstChild() ? beforeChild : 0); }
std::unique_ptr<GlyphToPathTranslator> SVGTextRunRenderingContext::createGlyphToPathTranslator(const SimpleFontData& fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(&fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return std::make_unique<DummyGlyphToPathTranslator>(); auto& elementRenderer = renderer().isRenderElement() ? toRenderElement(renderer()) : *renderer().parent(); RenderStyle& style = elementRenderer.style(); bool isVerticalText = style.svgStyle().isVerticalWritingMode(); float scale = scaleEmToUnits(fontData.platformData().size(), fontFaceElement->unitsPerEm()); return std::make_unique<SVGGlyphToPathTranslator>(glyphBuffer, point, *svgFontData, *fontElement, from, numGlyphs, scale, isVerticalText); }
void RenderNamedFlowFragment::setObjectStyleInRegion(RenderObject* object, PassRefPtr<RenderStyle> styleInRegion, bool objectRegionStyleCached) { ASSERT(object->flowThreadContainingBlock()); RefPtr<RenderStyle> objectOriginalStyle = &object->style(); if (object->isRenderElement()) toRenderElement(object)->setStyleInternal(*styleInRegion); if (object->isBoxModelObject() && !object->hasBoxDecorations()) { bool hasBoxDecorations = object->isTableCell() || object->style().hasBackground() || object->style().hasBorder() || object->style().hasAppearance() || object->style().boxShadow(); object->setHasBoxDecorations(hasBoxDecorations); } ObjectRegionStyleInfo styleInfo; styleInfo.style = objectOriginalStyle; styleInfo.cached = objectRegionStyleCached; m_renderObjectRegionStyle.set(object, styleInfo); }
void RenderFrameSet::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderObject* child = firstChild(); if (!child) return; LayoutPoint adjustedPaintOffset = paintOffset + location(); size_t rows = m_rows.m_sizes.size(); size_t cols = m_cols.m_sizes.size(); LayoutUnit borderThickness = frameSetElement().border(); LayoutUnit yPos = 0; for (size_t r = 0; r < rows; r++) { LayoutUnit xPos = 0; for (size_t c = 0; c < cols; c++) { toRenderElement(child)->paint(paintInfo, adjustedPaintOffset); xPos += m_cols.m_sizes[c]; if (borderThickness && m_cols.m_allowBorder[c + 1]) { paintColumnBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x() + xPos, adjustedPaintOffset.y() + yPos, borderThickness, height()))); xPos += borderThickness; } child = child->nextSibling(); if (!child) return; } yPos += m_rows.m_sizes[r]; if (borderThickness && m_rows.m_allowBorder[r + 1]) { paintRowBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x(), adjustedPaintOffset.y() + yPos, width(), borderThickness))); yPos += borderThickness; } } }
void RenderSVGRoot::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // An empty viewport disables rendering. if (pixelSnappedBorderBoxRect().isEmpty()) return; // Don't paint, if the context explicitly disabled it. if (paintInfo.context->paintingDisabled()) return; // An empty viewBox also disables rendering. // (http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute) if (svgSVGElement().hasEmptyViewBox()) return; Page* page = frame().page(); // Don't paint if we don't have kids, except if we have filters we should paint those. if (!firstChild()) { SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); if (!resources || !resources->filter()) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } } if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantRepaintedObject(this, visualOverflowRect()); // Make a copy of the PaintInfo because applyTransform will modify the damage rect. PaintInfo childPaintInfo(paintInfo); childPaintInfo.context->save(); // Apply initial viewport clip - not affected by overflow handling childPaintInfo.context->clip(pixelSnappedIntRect(overflowClipRect(paintOffset, paintInfo.renderRegion))); // Convert from container offsets (html renderers) to a relative transform (svg renderers). // Transform from our paint container's coordinate system to our local coords. IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset); childPaintInfo.applyTransform(AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()) * localToBorderBoxTransform()); // SVGRenderingContext must be destroyed before we restore the childPaintInfo.context, because a filter may have // changed the context and it is only reverted when the SVGRenderingContext destructor finishes applying the filter. { SVGRenderingContext renderingContext; bool continueRendering = true; if (childPaintInfo.phase == PaintPhaseForeground) { renderingContext.prepareToRenderSVGContent(*this, childPaintInfo); continueRendering = renderingContext.isRenderingPrepared(); } if (continueRendering) { childPaintInfo.updateSubtreePaintRootForChildren(this); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { // FIXME: Can this ever have RenderText children? if (!child->isRenderElement()) continue; toRenderElement(child)->paint(childPaintInfo, location()); } } } childPaintInfo.context->restore(); }
void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) { bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); bool transformChanged = transformToRootChanged(start); bool hasSVGShadow = rendererHasSVGShadow(start); bool needsBoundariesUpdate = start->needsBoundariesUpdate(); HashSet<RenderObject*> notlayoutedObjects; for (RenderObject* child = start->firstChildSlow(); child; child = child->nextSibling()) { bool needsLayout = selfNeedsLayout; bool childEverHadLayout = child->everHadLayout(); if (needsBoundariesUpdate && hasSVGShadow) { // If we have a shadow, our shadow is baked into our children's cached boundaries, // so they need to update. child->setNeedsBoundariesUpdate(); needsLayout = true; } if (transformChanged) { // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). if (child->isSVGText()) toRenderSVGText(child)->setNeedsTextMetricsUpdate(); needsLayout = true; } if (layoutSizeChanged) { // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { if (element->hasRelativeLengths()) { // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object if (child->isSVGShape()) toRenderSVGShape(child)->setNeedsShapeUpdate(); else if (child->isSVGText()) { toRenderSVGText(child)->setNeedsTextMetricsUpdate(); toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); } needsLayout = true; } } } if (needsLayout) child->setNeedsLayout(MarkOnlyThis); if (child->needsLayout()) { toRenderElement(child)->layout(); // Renderers are responsible for repainting themselves when changing, except // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. // We could handle this in the individual objects, but for now it's easier to have // parent containers call repaint(). (RenderBlock::layout* has similar logic.) if (!childEverHadLayout) child->repaint(); } else if (layoutSizeChanged) notlayoutedObjects.add(child); ASSERT(!child->needsLayout()); } if (!layoutSizeChanged) { ASSERT(notlayoutedObjects.isEmpty()); return; } // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) invalidateResourcesOfChildren(*it); }
bool SVGFontData::applySVGGlyphSelection(WidthIterator& iterator, GlyphData& glyphData, bool mirror, unsigned currentCharacter, unsigned& advanceLength) const { const TextRun& run = iterator.run(); Vector<SVGGlyph::ArabicForm>& arabicForms = iterator.arabicForms(); ASSERT(run.charactersLength() >= currentCharacter); // Associate text with arabic forms, if needed. String remainingTextInRun; if (run.is8Bit()) { remainingTextInRun = String(run.data8(currentCharacter), run.charactersLength() - currentCharacter); remainingTextInRun = Font::normalizeSpaces(remainingTextInRun.characters8(), remainingTextInRun.length()); } else { remainingTextInRun = String(run.data16(currentCharacter), run.charactersLength() - currentCharacter); remainingTextInRun = Font::normalizeSpaces(remainingTextInRun.characters16(), remainingTextInRun.length()); } if (mirror) remainingTextInRun = createStringWithMirroredCharacters(remainingTextInRun); if (!currentCharacter && arabicForms.isEmpty()) arabicForms = charactersWithArabicForm(remainingTextInRun, mirror); SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); ASSERT(svgFontFaceElement); SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); ASSERT(associatedFontElement); RenderObject* renderObject = 0; if (TextRun::RenderingContext* renderingContext = run.renderingContext()) renderObject = &static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer(); String language; bool isVerticalText = false; Vector<String> altGlyphNames; if (renderObject) { RenderElement* parentRenderer = renderObject->isRenderElement() ? toRenderElement(renderObject) : renderObject->parent(); ASSERT(parentRenderer); isVerticalText = parentRenderer->style().svgStyle().isVerticalWritingMode(); if (Element* parentRendererElement = parentRenderer->element()) { language = parentRendererElement->getAttribute(XMLNames::langAttr); if (isSVGAltGlyphElement(parentRendererElement)) { SVGAltGlyphElement* altGlyph = toSVGAltGlyphElement(parentRendererElement); if (!altGlyph->hasValidGlyphElements(altGlyphNames)) altGlyphNames.clear(); } } } Vector<SVGGlyph> glyphs; size_t altGlyphNamesSize = altGlyphNames.size(); if (altGlyphNamesSize) { for (size_t index = 0; index < altGlyphNamesSize; ++index) associatedFontElement->collectGlyphsForGlyphName(altGlyphNames[index], glyphs); // Assign the unicodeStringLength now that its known. size_t glyphsSize = glyphs.size(); for (size_t i = 0; i < glyphsSize; ++i) glyphs[i].unicodeStringLength = run.length(); // Do not check alt glyphs for compatibility. Just return the first one. // Later code will fail if we do not do this and the glyph is incompatible. if (glyphsSize) { SVGGlyph& svgGlyph = glyphs[0]; iterator.setLastGlyphName(svgGlyph.glyphName); glyphData.glyph = svgGlyph.tableEntry; advanceLength = svgGlyph.unicodeStringLength; return true; } } else associatedFontElement->collectGlyphsForString(remainingTextInRun, glyphs); size_t glyphsSize = glyphs.size(); for (size_t i = 0; i < glyphsSize; ++i) { SVGGlyph& svgGlyph = glyphs[i]; if (svgGlyph.isPartOfLigature) continue; if (!isCompatibleGlyph(svgGlyph, isVerticalText, language, arabicForms, currentCharacter, currentCharacter + svgGlyph.unicodeStringLength)) continue; iterator.setLastGlyphName(svgGlyph.glyphName); glyphData.glyph = svgGlyph.tableEntry; advanceLength = svgGlyph.unicodeStringLength; return true; } iterator.setLastGlyphName(String()); return false; }
GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, WidthIterator& iterator, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength) { const SimpleFontData* primaryFont = font.primaryFont(); ASSERT(primaryFont); pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror, AutoVariant); GlyphData glyphData = pair.first; // Check if we have the missing glyph data, in which case we can just return. GlyphData missingGlyphData = primaryFont->missingGlyphData(); if (glyphData.glyph == missingGlyphData.glyph && glyphData.fontData == missingGlyphData.fontData) { ASSERT(glyphData.fontData); return glyphData; } // Save data fromt he font fallback list because we may modify it later. Do this before the // potential change to glyphData.fontData below. FontGlyphs* glyph = font.glyphs(); ASSERT(glyph); FontGlyphs::GlyphPagesStateSaver glyphPagesSaver(*glyph); // Characters enclosed by an <altGlyph> element, may not be registered in the GlyphPage. const SimpleFontData* originalFontData = glyphData.fontData; if (glyphData.fontData && !glyphData.fontData->isSVGFont()) { auto& elementRenderer = renderer().isRenderElement() ? toRenderElement(renderer()) : *renderer().parent(); if (Element* parentRendererElement = elementRenderer.element()) { if (parentRendererElement->hasTagName(SVGNames::altGlyphTag)) glyphData.fontData = primaryFont; } } const SimpleFontData* fontData = glyphData.fontData; if (fontData) { if (!fontData->isSVGFont()) return glyphData; SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return glyphData; // If we got here, we're dealing with a glyph defined in a SVG Font. // The returned glyph by glyphDataAndPageForCharacter() is a glyph stored in the SVG Font glyph table. // This doesn't necessarily mean the glyph is suitable for rendering/measuring in this context, its // arabic-form/orientation/... may not match, we have to apply SVG Glyph selection to discover that. if (svgFontData->applySVGGlyphSelection(iterator, glyphData, mirror, currentCharacter, advanceLength)) return glyphData; } GlyphPage* page = pair.second; ASSERT(page); // No suitable glyph found that is compatible with the requirments (same language, arabic-form, orientation etc.) // Even though our GlyphPage contains an entry for eg. glyph "a", it's not compatible. So we have to temporarily // remove the glyph data information from the GlyphPage, and retry the lookup, which handles font fallbacks correctly. page->setGlyphDataForCharacter(character, 0, 0); // Assure that the font fallback glyph selection worked, aka. the fallbackGlyphData font data is not the same as before. GlyphData fallbackGlyphData = font.glyphDataForCharacter(character, mirror); ASSERT(fallbackGlyphData.fontData != fontData); // Restore original state of the SVG Font glyph table and the current font fallback list, // to assure the next lookup of the same glyph won't immediately return the fallback glyph. page->setGlyphDataForCharacter(character, glyphData.glyph, originalFontData); ASSERT(fallbackGlyphData.fontData); return fallbackGlyphData; }
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return; auto activePaintingResource = this->activePaintingResource(); if (!activePaintingResource) { // TODO: We're only supporting simple filled HTML text so far. RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource(); solidPaintingResource->setColor(context->fillColor()); activePaintingResource = solidPaintingResource; } auto& elementRenderer = renderer().isRenderElement() ? toRenderElement(renderer()) : *renderer().parent(); RenderStyle& style = *elementRenderer.style(); bool isVerticalText = style.svgStyle()->isVerticalWritingMode(); float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); ASSERT(activePaintingResource); FloatPoint glyphOrigin; glyphOrigin.setX(svgFontData->horizontalOriginX() * scale); glyphOrigin.setY(svgFontData->horizontalOriginY() * scale); FloatPoint currentPoint = point; RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); if (!glyph) continue; float advance = glyphBuffer.advanceAt(from + i).width(); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData); // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (svgGlyph.pathData.isEmpty()) { if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); continue; } if (isVerticalText) { glyphOrigin.setX(svgGlyph.verticalOriginX * scale); glyphOrigin.setY(svgGlyph.verticalOriginY * scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); glyphPathTransform.scale(scale, -scale); Path glyphPath = svgGlyph.pathData; glyphPath.transform(glyphPathTransform); if (activePaintingResource->applyResource(elementRenderer, &style, context, resourceMode)) { float strokeThickness = context->strokeThickness(); if (renderer().isSVGInlineText()) context->setStrokeThickness(strokeThickness * toRenderSVGInlineText(renderer()).scalingFactor()); activePaintingResource->postApplyResource(elementRenderer, context, resourceMode, &glyphPath, 0); context->setStrokeThickness(strokeThickness); } if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); } }