def getTextWithFields(self, formatConfig: Optional[Dict] = None) -> textInfos.TextInfo.TextWithFieldsT:
		# Get the initial control fields.
		fields = []
		rootObj = self.obj.rootNVDAObject
		obj = self._startObj
		while obj and obj != rootObj:
			field = self._getControlFieldForObject(obj)
			if field:
				fields.insert(0, textInfos.FieldCommand("controlStart", field))
			obj = obj.parent

		embedIndex = None
		for ti in self._getTextInfos():
			for textWithEmbeddedObjectsItem in ti._iterTextWithEmbeddedObjects(True, formatConfig=formatConfig):
				if isinstance(textWithEmbeddedObjectsItem, int):  # Embedded object
					if embedIndex is None:
						embedIndex = self._getFirstEmbedIndex(ti)
					else:
						embedIndex += 1
					childObject: NVDAObject = ti.obj.getChild(embedIndex)
					controlField = self._getControlFieldForObject(childObject, ignoreEditableText=False)
					controlField["content"] = childObject.name
					fields.extend((
						textInfos.FieldCommand("controlStart", controlField),
						textUtils.OBJ_REPLACEMENT_CHAR,
						textInfos.FieldCommand("controlEnd", None)
					))
				else:  # str or fieldCommand
					if not isinstance(textWithEmbeddedObjectsItem, (str, textInfos.FieldCommand)):
						log.error(f"Unexpected type: {textWithEmbeddedObjectsItem!r}")
					fields.append(textWithEmbeddedObjectsItem)
		return fields
Exemple #2
0
    def _startElementHandler(self, tagName, attrs):
        if tagName == 'unich':
            data = attrs.get('value', None)
            if data is not None:
                try:
                    data = chr(int(data))
                except ValueError:
                    data = textUtils.REPLACEMENT_CHAR
                self._CharacterDataHandler(
                    data, processBufferedSurrogates=isLowSurrogate(data))
            return
        elif tagName == 'control':
            newAttrs = textInfos.ControlField(attrs)
            self._commandList.append(
                textInfos.FieldCommand("controlStart", newAttrs))
        elif tagName == 'text':
            newAttrs = textInfos.FormatField(attrs)
            self._commandList.append(
                textInfos.FieldCommand("formatChange", newAttrs))
        else:
            raise ValueError("Unknown tag name: %s" % tagName)

        # Normalise attributes common to both field types.
        try:
            newAttrs["_startOfNode"] = newAttrs["_startOfNode"] == "1"
        except KeyError:
            pass
        try:
            newAttrs["_endOfNode"] = newAttrs["_endOfNode"] == "1"
        except KeyError:
            pass
Exemple #3
0
 def _getTextWithFieldsForRange(self, obj, rangeObj, formatConfig):
     #Graphics usually have no actual text, so render the name instead
     if rangeObj.CompareEndpoints(
             UIAHandler.TextPatternRangeEndpoint_Start, rangeObj, UIAHandler
             .TextPatternRangeEndpoint_End) == 0 and obj != self.obj:
         for x in obj.makeTextInfo("all").getTextWithFields(formatConfig):
             yield x
         return
     tempRange = rangeObj.clone()
     children = rangeObj.getChildren()
     for index in xrange(children.length):
         child = children.getElement(index)
         childObj = UIA(UIAElement=child.buildUpdatedCache(
             UIAHandler.handler.baseCacheRequest))
         #Sometimes a child range can contain the same children as its parent (causing an infinite loop)
         # Example: checkboxes, graphics, in Edge.
         if childObj == obj:
             continue
         childRange = self.obj.UIATextPattern.rangeFromChild(child)
         if childRange.CompareEndpoints(
                 UIAHandler.TextPatternRangeEndpoint_End, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_Start
         ) <= 0 or childRange.CompareEndpoints(
                 UIAHandler.TextPatternRangeEndpoint_Start, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_End) >= 0:
             continue
         if childRange.CompareEndpoints(
                 UIAHandler.TextPatternRangeEndpoint_Start, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_Start) < 0:
             childRange.moveEndpointByRange(
                 UIAHandler.TextPatternRangeEndpoint_Start, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_Start)
         if childRange.CompareEndpoints(
                 UIAHandler.TextPatternRangeEndpoint_End, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_End) > 0:
             childRange.moveEndpointByRange(
                 UIAHandler.TextPatternRangeEndpoint_End, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_End)
         tempRange.moveEndpointByRange(
             UIAHandler.TextPatternRangeEndpoint_End, childRange,
             UIAHandler.TextPatternRangeEndpoint_Start)
         for f in self._getFormatFieldsAndText(tempRange, formatConfig):
             yield f
         field = self._getControlFieldForObject(
             childObj) if childObj else None
         if field:
             yield textInfos.FieldCommand("controlStart", field)
         for x in self._getTextWithFieldsForRange(childObj, childRange,
                                                  formatConfig):
             yield x
         if field:
             yield textInfos.FieldCommand("controlEnd", None)
         tempRange.moveEndpointByRange(
             UIAHandler.TextPatternRangeEndpoint_Start, childRange,
             UIAHandler.TextPatternRangeEndpoint_End)
     tempRange.moveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,
                                   rangeObj,
                                   UIAHandler.TextPatternRangeEndpoint_End)
     for f in self._getFormatFieldsAndText(tempRange, formatConfig):
         yield f
Exemple #4
0
 def getTextWithFields(self, formatConfig=None):
     if not formatConfig:
         formatConfig = config.conf["documentFormatting"]
     fields = []
     try:
         e = self._rangeObj.getEnclosingElement().buildUpdatedCache(
             UIAHandler.handler.baseCacheRequest)
     except COMError:
         e = None
     fields = []
     if e:
         obj = UIA(UIAElement=e)
         while obj and obj != self.obj:
             try:
                 field = self._getControlFieldForObject(obj)
             except LookupError:
                 break
             if field:
                 field = textInfos.FieldCommand("controlStart", field)
                 fields.append(field)
             obj = obj.parent
         fields.reverse()
     ancestorCount = len(fields)
     fields.extend(
         self._getTextWithFieldsForRange(self.obj, self._rangeObj,
                                         formatConfig))
     fields.extend(
         textInfos.FieldCommand("controlEnd", None)
         for x in xrange(ancestorCount))
     return fields
    def getTextWithFields(self, formatConfig=None):
        # Get the initial control fields.
        fields = []
        rootObj = self.obj.rootNVDAObject
        obj = self._startObj
        while obj and obj != rootObj:
            field = self._getControlFieldForObject(obj)
            if field:
                fields.insert(0, textInfos.FieldCommand("controlStart", field))
            obj = obj.parent

        for ti in self._getTextInfos():
            fieldStart = 0
            for field in ti.getTextWithFields(formatConfig=formatConfig):
                if isinstance(field, basestring):
                    textLength = len(field)
                    for chunk in self._iterTextWithEmbeddedObjects(
                            field, ti, fieldStart, textLength=textLength):
                        if isinstance(chunk, basestring):
                            fields.append(chunk)
                        else:
                            controlField = self._getControlFieldForObject(
                                chunk, ignoreEditableText=False)
                            controlField["alwaysReportName"] = True
                            fields.extend(
                                (textInfos.FieldCommand(
                                    "controlStart", controlField), u"\uFFFC",
                                 textInfos.FieldCommand("controlEnd", None)))
                    fieldStart += textLength

                else:
                    fields.append(field)
        return fields
	def getTextWithFields(self, formatConfig=None):
		# Get the initial control fields.
		fields = []
		rootObj = self.obj.rootNVDAObject
		obj = self._startObj
		while obj and obj != rootObj:
			field = self._getControlFieldForObject(obj)
			if field:
				fields.insert(0, textInfos.FieldCommand("controlStart", field))
			obj = obj.parent

		embedIndex = None
		for ti in self._getTextInfos():
			for field in ti._iterTextWithEmbeddedObjects(True, formatConfig=formatConfig):
				if isinstance(field, basestring):
					fields.append(field)
				elif isinstance(field, int): # Embedded object
					if embedIndex is None:
						embedIndex = self._getFirstEmbedIndex(ti)
					else:
						embedIndex += 1
					field = ti.obj.getChild(embedIndex)
					controlField = self._getControlFieldForObject(field, ignoreEditableText=False)
					controlField["content"] = field.name
					fields.extend((textInfos.FieldCommand("controlStart", controlField),
						u"\uFFFC",
						textInfos.FieldCommand("controlEnd", None)))
				else:
					fields.append(field)
		return fields
Exemple #7
0
    def _iterRecursiveText(self, ti: offsets.OffsetsTextInfo, controlStack,
                           formatConfig):
        if ti.obj == self._endObj:
            end = True
            ti.setEndPoint(self._end, "endToEnd")
        else:
            end = False

        for item in ti._iterTextWithEmbeddedObjects(controlStack is not None,
                                                    formatConfig=formatConfig):
            if item is None:
                yield u""
            elif isinstance(item, str):
                yield item
            elif isinstance(item, int):  # Embedded object.
                embedded: typing.Optional[IAccessible] = _getEmbedded(
                    ti.obj, item)
                if embedded is None:
                    continue
                notText = _getRawTextInfo(embedded) is NVDAObjectTextInfo
                if controlStack is not None:
                    controlField = self._getControlFieldForObject(embedded)
                    controlStack.append(controlField)
                    if controlField:
                        if notText:
                            controlField["content"] = embedded.name
                        controlField["_startOfNode"] = True
                        yield textInfos.FieldCommand("controlStart",
                                                     controlField)
                if notText:
                    # A 'stand-in' character is necessary to make routing work on braille devices.
                    # Note #11291:
                    # Using a space character (EG " ") causes 'space' to be announced after objects like graphics.
                    # If this is replaced with an empty string, routing to cell becomes innaccurate.
                    # Using the textUtils.OBJ_REPLACEMENT_CHAR which is the
                    # "OBJECT REPLACEMENT CHARACTER" (EG "\uFFFC")
                    # results in '"0xFFFC' being displayed on the braille device.
                    yield " "
                else:
                    for subItem in self._iterRecursiveText(
                            self._makeRawTextInfo(embedded,
                                                  textInfos.POSITION_ALL),
                            controlStack, formatConfig):
                        yield subItem
                        if subItem is None:
                            return
                if controlStack is not None and controlField:
                    controlField["_endOfNode"] = True
                    del controlStack[-1]
                    yield textInfos.FieldCommand("controlEnd", None)
            else:
                yield item

        if end:
            # None means the end has been reached and text retrieval should stop.
            yield None
Exemple #8
0
 def _getTextWithFieldsForRange(self, obj, rangeObj, formatConfig):
     #Graphics usually have no actual text, so render the name instead
     if obj.role == controlTypes.ROLE_GRAPHIC:
         yield obj.name
         return
     tempRange = rangeObj.clone()
     children = rangeObj.getChildren()
     for index in xrange(children.length):
         child = children.getElement(index)
         childObj = UIA(UIAElement=child.buildUpdatedCache(
             UIAHandler.handler.baseCacheRequest))
         #Sometimes a child range can contain the same children as its parent (causing an infinite loop)
         if childObj == obj:
             continue
         childRange = self.obj.UIATextPattern.rangeFromChild(child)
         if childRange.CompareEndpoints(
                 UIAHandler.TextPatternRangeEndpoint_Start, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_Start) < 0:
             childRange.moveEndpointByRange(
                 UIAHandler.TextPatternRangeEndpoint_Start, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_Start)
         if childRange.CompareEndpoints(
                 UIAHandler.TextPatternRangeEndpoint_End, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_End) > 0:
             childRange.moveEndpointByRange(
                 UIAHandler.TextPatternRangeEndpoint_End, rangeObj,
                 UIAHandler.TextPatternRangeEndpoint_End)
         tempRange.moveEndpointByRange(
             UIAHandler.TextPatternRangeEndpoint_End, childRange,
             UIAHandler.TextPatternRangeEndpoint_Start)
         text = tempRange.getText(-1)
         if text:
             yield self._getFormatFieldAtRange(tempRange, formatConfig)
             yield text
         field = self._getControlFieldForObject(
             childObj) if childObj else None
         if field:
             yield textInfos.FieldCommand("controlStart", field)
         for x in self._getTextWithFieldsForRange(childObj, childRange,
                                                  formatConfig):
             yield x
         if field:
             yield textInfos.FieldCommand("controlEnd", None)
         tempRange.moveEndpointByRange(
             UIAHandler.TextPatternRangeEndpoint_Start, childRange,
             UIAHandler.TextPatternRangeEndpoint_End)
     tempRange.moveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,
                                   rangeObj,
                                   UIAHandler.TextPatternRangeEndpoint_End)
     text = tempRange.getText(-1)
     if text:
         yield self._getFormatFieldAtRange(tempRange, formatConfig)
         yield text
 def getTextWithFields(self, formatConfig=None):
     commands = []
     if self.isCollapsed:
         return commands
     if not formatConfig:
         formatConfig = config.conf["documentFormatting"]
     left, top = self._consoleCoordFromOffset(self._startOffset)
     right, bottom = self._consoleCoordFromOffset(self._endOffset - 1)
     rect = wincon.SMALL_RECT(left, top, right, bottom)
     if bottom - top > 0:  #offsets span multiple lines
         rect.Left = 0
         rect.Right = self.consoleScreenBufferInfo.dwSize.x - 1
         length = self.consoleScreenBufferInfo.dwSize.x * (bottom - top + 1)
     else:
         length = self._endOffset - self._startOffset
     buf = wincon.ReadConsoleOutput(consoleOutputHandle, length, rect)
     if bottom - top > 0:
         buf = buf[left:len(buf) -
                   (self.consoleScreenBufferInfo.dwSize.x - right) + 1]
     lastAttr = None
     lastText = []
     boundEnd = self._startOffset
     for i, c in enumerate(buf):
         if self._startOffset + i == boundEnd:
             field, (boundStart, boundEnd) = self._getFormatFieldAndOffsets(
                 boundEnd, formatConfig)
             if lastText:
                 commands.append("".join(lastText))
                 lastText = []
             commands.append(textInfos.FieldCommand("formatChange", field))
         if not c.Attributes == lastAttr:
             formatField = textInfos.FormatField()
             if formatConfig['reportColor']:
                 formatField["color"] = CONSOLE_COLORS_TO_RGB[c.Attributes
                                                              & 0x0f]
                 formatField["background-color"] = CONSOLE_COLORS_TO_RGB[
                     (c.Attributes >> 4) & 0x0f]
             if formatConfig[
                     'reportFontAttributes'] and c.Attributes & COMMON_LVB_UNDERSCORE:
                 formatField['underline'] = True
             if formatField:
                 if lastText:
                     commands.append("".join(lastText))
                     lastText = []
                 command = textInfos.FieldCommand("formatChange",
                                                  formatField)
                 commands.append(command)
             lastAttr = c.Attributes
         lastText.append(c.Char)
     commands.append("".join(lastText))
     return commands
Exemple #10
0
 def _getFormatFieldAtRange(self, range, formatConfig):
     formatField = textInfos.FormatField()
     if formatConfig["reportFontName"]:
         try:
             fontNameValue = range.GetAttributeValue(
                 UIAHandler.UIA_FontNameAttributeId)
         except COMError:
             fontNameValue = UIAHandler.handler.reservedNotSupportedValue
         if fontNameValue != UIAHandler.handler.reservedNotSupportedValue:
             formatField["font-name"] = fontNameValue
     if formatConfig["reportFontSize"]:
         try:
             fontSizeValue = range.GetAttributeValue(
                 UIAHandler.UIA_FontSizeAttributeId)
         except COMError:
             fontSizeValue = UIAHandler.handler.reservedNotSupportedValue
         if fontSizeValue != UIAHandler.handler.reservedNotSupportedValue:
             formatField['font-size'] = "%g pt" % float(fontSizeValue)
     if formatConfig["reportHeadings"]:
         try:
             styleIDValue = range.GetAttributeValue(
                 UIAHandler.UIA_StyleIdAttributeId)
         except COMError:
             styleIDValue = UIAHandler.handler.reservedNotSupportedValue
         if UIAHandler.StyleId_Heading1 <= styleIDValue <= UIAHandler.StyleId_Heading9:
             formatField["heading-level"] = (
                 styleIDValue - UIAHandler.StyleId_Heading1) + 1
     return textInfos.FieldCommand("formatChange", formatField)
	def _EndElementHandler(self,tagName):
		if tagName=="control":
			self._commandList.append(textInfos.FieldCommand("controlEnd",None))
		elif tagName in ("text","unich"):
			pass
		else:
			raise ValueError("unknown tag name: %s"%tagName)
Exemple #12
0
    def _iterRecursiveText(self, ti, controlStack, formatConfig):
        if ti.obj == self._endObj:
            end = True
            ti.setEndPoint(self._end, "endToEnd")
        else:
            end = False

        for item in ti._iterTextWithEmbeddedObjects(controlStack is not None,
                                                    formatConfig=formatConfig):
            if item is None:
                yield u""
            elif isinstance(item, basestring):
                yield item
            elif isinstance(item, int):  # Embedded object.
                embedded = _getEmbedded(ti.obj, item)
                notText = _getRawTextInfo(embedded) is NVDAObjectTextInfo
                if controlStack is not None:
                    controlField = self._getControlFieldForObject(embedded)
                    controlStack.append(controlField)
                    if controlField:
                        if notText:
                            controlField["content"] = embedded.name
                        controlField["_startOfNode"] = True
                        yield textInfos.FieldCommand("controlStart",
                                                     controlField)
                if notText:
                    yield u" "
                else:
                    for subItem in self._iterRecursiveText(
                            self._makeRawTextInfo(embedded,
                                                  textInfos.POSITION_ALL),
                            controlStack, formatConfig):
                        yield subItem
                        if subItem is None:
                            return
                if controlStack is not None and controlField:
                    controlField["_endOfNode"] = True
                    del controlStack[-1]
                    yield textInfos.FieldCommand("controlEnd", None)
            else:
                yield item

        if end:
            # None means the end has been reached and text retrieval should stop.
            yield None
Exemple #13
0
	def getTextWithFields(self,formatConfig=None):
		if not formatConfig:
			formatConfig=config.conf["documentFormatting"]
		if self.detectFormattingAfterCursorMaybeSlow and not formatConfig['detectFormatAfterCursor']:
			field,(boundStart,boundEnd)=self._getFormatFieldAndOffsets(self._startOffset,formatConfig,calculateOffsets=False)
			text=self.text
			return [textInfos.FieldCommand('formatChange',field),text]
		commandList=[]
		offset=self._startOffset
		while offset<self._endOffset:
			field,(boundStart,boundEnd)=self._getFormatFieldAndOffsets(offset,formatConfig)
			if boundEnd<=boundStart:
				boundEnd=boundStart+1
			if boundEnd<=offset:
				boundEnd=offset+1
			command=textInfos.FieldCommand("formatChange",field)
			commandList.append(command)
			text=self._getTextRange(offset,min(boundEnd,self._endOffset))
			commandList.append(text)
			offset=boundEnd
		return commandList
Exemple #14
0
	def getTextWithFields(self, formatConfig=None):
		# Get the initial control fields.
		fields = []
		rootObj = self.obj.rootNVDAObject
		obj = self._startObj
		while obj and obj != rootObj:
			if not (
				self._isObjectEditableText(obj)
				or self._isNamedlinkDestination(obj)
			):
				field = self._getControlFieldForObject(obj)
				fields.insert(0, textInfos.FieldCommand("controlStart", field))
			obj = obj.parent

		embedIndex = None
		for ti in self._getTextInfos():
			for textWithEmbeddedObjectsItem in ti._iterTextWithEmbeddedObjects(True, formatConfig=formatConfig):
				if isinstance(textWithEmbeddedObjectsItem, int):  # Embedded object
					if embedIndex is None:
						embedIndex = self._getFirstEmbedIndex(ti)
					else:
						embedIndex += 1
					childObject: NVDAObject = ti.obj.getChild(embedIndex)
					if not (
						# Don't check for self._isObjectEditableText
						# Only for named link destinations.
						self._isNamedlinkDestination(obj)
					):
						controlField = self._getControlFieldForObject(childObject)
						controlField["content"] = childObject.name
						fields.extend((
							textInfos.FieldCommand("controlStart", controlField),
							textUtils.OBJ_REPLACEMENT_CHAR,
							textInfos.FieldCommand("controlEnd", None),
						))
				else:  # str or fieldCommand
					if not isinstance(textWithEmbeddedObjectsItem, (str, textInfos.FieldCommand)):
						log.error(f"Unexpected type: {textWithEmbeddedObjectsItem!r}")
					fields.append(textWithEmbeddedObjectsItem)
		return fields
Exemple #15
0
 def _getFormatFieldsAndText(self, tempRange, formatConfig):
     if not self.allowGetFormatFieldsAndTextOnDegenerateUIARanges and tempRange.compareEndpoints(
             UIAHandler.TextPatternRangeEndpoint_Start, tempRange,
             UIAHandler.TextPatternRangeEndpoint_End) == 0:
         return
     formatField = self._getFormatFieldAtRange(tempRange, formatConfig)
     if formatConfig["reportSpellingErrors"]:
         try:
             annotationTypes = tempRange.GetAttributeValue(
                 UIAHandler.UIA_AnnotationTypesAttributeId)
         except COMError:
             annotationTypes = UIAHandler.handler.reservedNotSupportedValue
         if annotationTypes == UIAHandler.AnnotationType_SpellingError:
             formatField.field["invalid-spelling"] = True
             yield formatField
             yield tempRange.GetText(-1)
         elif annotationTypes == UIAHandler.handler.ReservedMixedAttributeValue:
             for r in self._iterUIARangeByUnit(tempRange,
                                               UIAHandler.TextUnit_Word):
                 text = r.GetText(-1)
                 if not text:
                     continue
                 r.MoveEndpointByRange(
                     UIAHandler.TextPatternRangeEndpoint_End, r,
                     UIAHandler.TextPatternRangeEndpoint_Start)
                 r.ExpandToEnclosingUnit(UIAHandler.TextUnit_Character)
                 try:
                     annotationTypes = r.GetAttributeValue(
                         UIAHandler.UIA_AnnotationTypesAttributeId)
                 except COMError:
                     annotationTypes = UIAHandler.handler.reservedNotSupportedValue
                 newField = textInfos.FormatField()
                 newField.update(formatField.field)
                 if annotationTypes == UIAHandler.AnnotationType_SpellingError:
                     newField["invalid-spelling"] = True
                 yield textInfos.FieldCommand("formatChange", newField)
                 yield text
         else:
             yield formatField
             yield tempRange.GetText(-1)
     else:
         yield formatField
         yield tempRange.GetText(-1)
Exemple #16
0
	def _getText(self, withFields, formatConfig=None):
		fields = []
		if self.isCollapsed:
			return fields

		if withFields:
			# Get the initial control fields.
			controlStack = []
			rootObj = self.obj
			obj = self._startObj
			ti = self._start
			cannotBeStart = False
			while obj and obj != rootObj:
				field = self._getControlFieldForObject(obj)
				if field:
					if ti._startOffset == 0:
						if not cannotBeStart:
							field["_startOfNode"] = True
					else:
						# We're not at the start of this object, which also means we're not at the start of any ancestors.
						cannotBeStart = True
					fields.insert(0, textInfos.FieldCommand("controlStart", field))
				controlStack.insert(0, field)
				ti = self._getEmbedding(obj)
				obj = ti.obj
		else:
			controlStack = None

		# Get the fields for start.
		fields += list(self._iterRecursiveText(self._start, controlStack, formatConfig))
		if not fields:
			# We're not getting anything, so the object must be dead.
			# (We already handled collapsed above.)
			return fields
		obj = self._startObj
		while fields[-1] is not None:
			# The end hasn't yet been reached, which means it isn't a descendant of obj.
			# Therefore, continue from where obj was embedded.
			if withFields:
				field = controlStack.pop()
				if field:
					# This object had a control field.
					field["_endOfNode"] = True
					fields.append(textInfos.FieldCommand("controlEnd", None))
			ti = self._getEmbedding(obj)
			obj = ti.obj
			if ti.move(textInfos.UNIT_OFFSET, 1) == 0:
				# There's no more text in this object.
				continue
			ti.setEndPoint(self._makeRawTextInfo(obj, textInfos.POSITION_ALL), "endToEnd")
			fields.extend(self._iterRecursiveText(ti, controlStack, formatConfig))
		del fields[-1]

		if withFields:
			# Determine whether the range covers the end of any ancestors of endObj.
			obj = self._endObj
			ti = self._end
			while obj and obj != rootObj:
				field = controlStack.pop()
				if field:
					fields.append(textInfos.FieldCommand("controlEnd", None))
					if ti.compareEndPoints(self._makeRawTextInfo(obj, textInfos.POSITION_ALL), "endToEnd") == 0:
						field["_endOfNode"] = True
					else:
						# We're not at the end of this object, which also means we're not at the end of any ancestors.
						break
				ti = self._getEmbedding(obj)
				obj = ti.obj

		return fields
Exemple #17
0
	def _getTextWithFieldsForUIARange(self,rootElement,textRange,formatConfig,includeRoot=True,recurseChildren=True,alwaysWalkAncestors=True,_rootElementClipped=(True,True)):
		# Edge zooms into its children at the start.
		# Thus you are already in the deepest first child.
		# Therefore get the deepest enclosing element at the start, get its content, Then do the whole thing again on the content from the end of the enclosing element to the end of its parent, and repete!
		# In other words, get the content while slowly zooming out from the start.
		log.debug("_getTextWithFieldsForUIARange (unbalanced)")
		if not recurseChildren:
			log.debug("recurseChildren is False. Falling back to super")
			for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(rootElement,textRange,formatConfig,includeRoot=includeRoot,alwaysWalkAncestors=True,recurseChildren=False,_rootElementClipped=_rootElementClipped):
				yield field
			return
		if log.isEnabledFor(log.DEBUG):
			log.debug("rootElement: %s"%rootElement.currentLocalizedControlType)
			log.debug("full text: %s"%textRange.getText(-1))
			log.debug("includeRoot: %s"%includeRoot)
		startRange=textRange.clone()
		startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_Start)
		enclosingElement=getEnclosingElementWithCacheFromUIATextRange(startRange,self._controlFieldUIACacheRequest)
		if not enclosingElement:
			log.debug("No enclosingElement. Returning")
			return
		enclosingRange=self.obj.getNormalizedUIATextRangeFromElement(enclosingElement)
		if not enclosingRange:
			log.debug("enclosingRange is NULL. Returning")
			return
		if log.isEnabledFor(log.DEBUG):
			log.debug("enclosingElement: %s"%enclosingElement.currentLocalizedControlType)
		startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,enclosingRange,UIAHandler.TextPatternRangeEndpoint_End)
		if startRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)>0:
			startRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)
		# Ensure we don't now have a collapsed range
		if startRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_Start)<=0:
			log.debug("Collapsed range. Returning")
			return
		# check for an embedded child
		childElements=getChildrenWithCacheFromUIATextRange(startRange,self._controlFieldUIACacheRequest)
		if childElements.length==1 and UIAHandler.handler.clientObject.compareElements(rootElement,childElements.getElement(0)):
			log.debug("Using single embedded child as enclosingElement")
			for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(rootElement,startRange,formatConfig,_rootElementClipped=_rootElementClipped,includeRoot=includeRoot,alwaysWalkAncestors=False,recurseChildren=False):
				yield field
			return
		parents=[]
		parentElement=enclosingElement
		log.debug("Generating ancestors:")
		hasAncestors=False
		while parentElement:
			if log.isEnabledFor(log.DEBUG):
				log.debug("parentElement: %s"%parentElement.currentLocalizedControlType)
			isRoot=UIAHandler.handler.clientObject.compareElements(parentElement,rootElement)
			log.debug("isRoot: %s"%isRoot)
			if not isRoot:
				hasAncestors=True
			if parentElement is not enclosingElement:
				if includeRoot or not isRoot:
					try:
						obj=UIA(windowHandle=self.obj.windowHandle,UIAElement=parentElement,initialUIACachedPropertyIDs=self._controlFieldUIACachedPropertyIDs)
						field=self._getControlFieldForObject(obj)
					except LookupError:
						log.debug("Failed to fetch controlField data for parentElement. Breaking")
						break
					parents.append((parentElement,field))
				else:
					# This is the root but it was not requested for inclusion
					# However we still need the root element itself for further recursion
					parents.append((parentElement,None))
			if isRoot:
				log.debug("Hit root. Breaking")
				break
			log.debug("Fetching next parentElement")
			parentElement=UIAHandler.handler.baseTreeWalker.getParentElementBuildCache(parentElement,self._controlFieldUIACacheRequest)
		log.debug("Done generating parents")
		log.debug("Yielding parents in reverse order")
		for parentElement,field in reversed(parents):
			if field: yield textInfos.FieldCommand("controlStart",field)
		log.debug("Done yielding parents")
		log.debug("Yielding balanced fields for startRange")
		clippedStart=enclosingRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_Start,startRange,UIAHandler.TextPatternRangeEndpoint_Start)<0
		clippedEnd=enclosingRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,startRange,UIAHandler.TextPatternRangeEndpoint_End)>0
		for field in super(EdgeTextInfo,self)._getTextWithFieldsForUIARange(enclosingElement,startRange,formatConfig,_rootElementClipped=(clippedStart,clippedEnd),includeRoot=includeRoot or hasAncestors,alwaysWalkAncestors=False,recurseChildren=True):
			yield field
		tempRange=startRange.clone()
		log.debug("Walking parents to yield controlEnds and recurse unbalanced endRanges")
		for parentElement,field in parents:
			if log.isEnabledFor(log.DEBUG):
				log.debug("parentElement: %s"%parentElement.currentLocalizedControlType)
			tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_Start,tempRange,UIAHandler.TextPatternRangeEndpoint_End)
			parentRange=self.obj.getNormalizedUIATextRangeFromElement(parentElement)
			if parentRange:
				tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,parentRange,UIAHandler.TextPatternRangeEndpoint_End)
				if tempRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)>0:
					tempRange.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,textRange,UIAHandler.TextPatternRangeEndpoint_End)
					clippedEnd=True
				else:
					clippedEnd=False
				if field:
					clippedStart=parentRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_Start,textRange,UIAHandler.TextPatternRangeEndpoint_Start)<0
					field['_startOfNode']=not clippedStart
					field['_endOfNode']=not clippedEnd
				if tempRange.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_End,tempRange,UIAHandler.TextPatternRangeEndpoint_Start)>0:
					log.debug("Recursing endRange")
					for endField in self._getTextWithFieldsForUIARange(parentElement,tempRange,formatConfig,_rootElementClipped=(clippedStart,clippedEnd),includeRoot=False,alwaysWalkAncestors=True,recurseChildren=True):
						yield endField
					log.debug("Done recursing endRange")
				else:
					log.debug("No content after parent")
			if field:
				log.debug("Yielding controlEnd for parent")
				yield textInfos.FieldCommand("controlEnd",field)
		log.debug("Done walking parents to yield controlEnds and recurse unbalanced endRanges")
		log.debug("_getTextWithFieldsForUIARange (unbalanced) end")
Exemple #18
0
 def getTextWithFields(self, formatConfig=None):
     if self.isCollapsed:
         # #7652: We cannot fetch fields on collapsed ranges otherwise we end up with repeating controlFields in braille (such as list list list).
         return []
     fields = super(WordDocumentTextInfo,
                    self).getTextWithFields(formatConfig=formatConfig)
     if len(fields) == 0:
         # Nothing to do... was probably a collapsed range.
         return fields
     # Sometimes embedded objects and graphics In MS Word can cause a controlStart then a controlEnd with no actual formatChange / text in the middle.
     # SpeakTextInfo always expects that the first lot of controlStarts will always contain some text.
     # Therefore ensure that the first lot of controlStarts does contain some text by inserting a blank formatChange and empty string in this case.
     for index in range(len(fields)):
         field = fields[index]
         if isinstance(field, textInfos.FieldCommand
                       ) and field.command == "controlStart":
             continue
         elif isinstance(
                 field,
                 textInfos.FieldCommand) and field.command == "controlEnd":
             formatChange = textInfos.FieldCommand("formatChange",
                                                   textInfos.FormatField())
             fields.insert(index, formatChange)
             fields.insert(index + 1, "")
         break
     ##7971: Microsoft Word exposes list bullets as part of the actual text.
     # This then confuses NVDA's braille cursor routing as it expects that there is a one-to-one mapping between characters in the text string and   unit character moves.
     # Therefore, detect when at the start of a list, and strip the bullet from the text string, placing it in the text's formatField as line-prefix.
     listItemStarted = False
     lastFormatField = None
     for index in range(len(fields)):
         field = fields[index]
         if isinstance(field, textInfos.FieldCommand
                       ) and field.command == "controlStart":
             if field.field.get(
                     'role'
             ) == controlTypes.ROLE_LISTITEM and field.field.get(
                     '_startOfNode'):
                 # We are in the start of a list item.
                 listItemStarted = True
         elif isinstance(field, textInfos.FieldCommand
                         ) and field.command == "formatChange":
             # This is the most recent formatField we have seen.
             lastFormatField = field.field
         elif listItemStarted and isinstance(field, str):
             # This is the first text string within the list.
             # Remove the text up to the first space, and store it as line-prefix which NVDA will appropriately speak/braille as a bullet.
             try:
                 spaceIndex = field.index(' ')
             except ValueError:
                 log.debugWarning("No space found in this text string")
                 break
             prefix = field[0:spaceIndex]
             fields[index] = field[spaceIndex + 1:]
             lastFormatField['line-prefix'] = prefix
             # Let speech know that line-prefix is safe to be spoken always, as it will only be exposed on the very first formatField on the list item.
             lastFormatField['line-prefix_speakAlways'] = True
             break
         else:
             # Not a controlStart, formatChange or text string. Nothing to do.
             break
     # Fill in page number attributes where NVDA expects
     try:
         page = fields[0].field['page-number']
     except KeyError:
         page = None
     if page is not None:
         for field in fields:
             if isinstance(field, textInfos.FieldCommand) and isinstance(
                     field.field, textInfos.FormatField):
                 field.field['page-number'] = page
     # MS Word can sometimes return a higher ancestor in its textRange's children.
     # E.g. a table inside a table header.
     # This does not cause a loop, but does cause information to be doubled
     # Detect these duplicates and remove them from the generated fields.
     seenStarts = set()
     pendingRemoves = []
     index = 0
     for index, field in enumerate(fields):
         if isinstance(field, textInfos.FieldCommand
                       ) and field.command == "controlStart":
             runtimeID = field.field['runtimeID']
             if not runtimeID:
                 continue
             if runtimeID in seenStarts:
                 pendingRemoves.append(field.field)
             else:
                 seenStarts.add(runtimeID)
         elif seenStarts:
             seenStarts.clear()
     index = 0
     while index < len(fields):
         field = fields[index]
         if isinstance(field, textInfos.FieldCommand) and any(
                 x is field.field for x in pendingRemoves):
             del fields[index]
         else:
             index += 1
     return fields
Exemple #19
0
    def getTextWithFields(  # noqa: C901
        self,
        formatConfig: Optional[Dict] = None
    ) -> textInfos.TextInfo.TextWithFieldsT:
        fields = None
        # #11043: when a non-collapsed text range is positioned within a blank table cell
        # MS Word does not return the table  cell as an enclosing element,
        # Thus NVDa thinks the range is not inside the cell.
        # This can be detected by asking for the first 2 characters of the range's text,
        # Which will either be an empty string, or the single end-of-row mark.
        # Anything else means it is not on an empty table cell,
        # or the range really does span more than the cell itself.
        # If this situation is detected,
        # copy and collapse the range, and fetch the content from that instead,
        # As a collapsed range on an empty cell does correctly return the table cell as its first enclosing element.
        if not self.isCollapsed:
            rawText = self._rangeObj.GetText(2)
            if not rawText or rawText == END_OF_ROW_MARK:
                r = self.copy()
                r.end = r.start
                fields = super(WordDocumentTextInfo,
                               r).getTextWithFields(formatConfig=formatConfig)
        if fields is None:
            fields = super().getTextWithFields(formatConfig=formatConfig)
        if len(fields) == 0:
            # Nothing to do... was probably a collapsed range.
            return fields

        # MS Word tries to produce speakable math content within equations.
        # However, using mathPlayer with the exposed mathml property on the equation is much nicer.
        # But, we therefore need to remove the inner math content if reading by line
        if not formatConfig or not formatConfig.get('extraDetail'):
            # We really only want to remove content if we can guarantee that mathPlayer is available.
            mathPres.ensureInit()
            if mathPres.speechProvider or mathPres.brailleProvider:
                curLevel = 0
                mathLevel = None
                mathStartIndex = None
                mathEndIndex = None
                for index in range(len(fields)):
                    field = fields[index]
                    if isinstance(field, textInfos.FieldCommand
                                  ) and field.command == "controlStart":
                        curLevel += 1
                        if mathLevel is None and field.field.get('mathml'):
                            mathLevel = curLevel
                            mathStartIndex = index
                    elif isinstance(field, textInfos.FieldCommand
                                    ) and field.command == "controlEnd":
                        if curLevel == mathLevel:
                            mathEndIndex = index
                        curLevel -= 1
                if mathEndIndex is not None:
                    del fields[mathStartIndex + 1:mathEndIndex]

        # Sometimes embedded objects and graphics In MS Word can cause a controlStart then a controlEnd with no actual formatChange / text in the middle.
        # SpeakTextInfo always expects that the first lot of controlStarts will always contain some text.
        # Therefore ensure that the first lot of controlStarts does contain some text by inserting a blank formatChange and empty string in this case.
        for index in range(len(fields)):
            field = fields[index]
            if isinstance(field, textInfos.FieldCommand
                          ) and field.command == "controlStart":
                continue
            elif isinstance(
                    field,
                    textInfos.FieldCommand) and field.command == "controlEnd":
                formatChange = textInfos.FieldCommand("formatChange",
                                                      textInfos.FormatField())
                fields.insert(index, formatChange)
                fields.insert(index + 1, "")
            break
        ##7971: Microsoft Word exposes list bullets as part of the actual text.
        # This then confuses NVDA's braille cursor routing as it expects that there is a one-to-one mapping between characters in the text string and   unit character moves.
        # Therefore, detect when at the start of a list, and strip the bullet from the text string, placing it in the text's formatField as line-prefix.
        listItemStarted = False
        lastFormatField = None
        for index in range(len(fields)):
            field = fields[index]
            if isinstance(field, textInfos.FieldCommand
                          ) and field.command == "controlStart":
                if field.field.get(
                        'role'
                ) == controlTypes.Role.LISTITEM and field.field.get(
                        '_startOfNode'):
                    # We are in the start of a list item.
                    listItemStarted = True
            elif isinstance(field, textInfos.FieldCommand
                            ) and field.command == "formatChange":
                # This is the most recent formatField we have seen.
                lastFormatField = field.field
            elif listItemStarted and isinstance(field, str):
                # This is the first text string within the list.
                # Remove the text up to the first space, and store it as line-prefix which NVDA will appropriately speak/braille as a bullet.
                try:
                    spaceIndex = field.index(' ')
                except ValueError:
                    log.debugWarning("No space found in this text string")
                    break
                prefix = field[0:spaceIndex]
                fields[index] = field[spaceIndex + 1:]
                lastFormatField['line-prefix'] = prefix
                # Let speech know that line-prefix is safe to be spoken always, as it will only be exposed on the very first formatField on the list item.
                lastFormatField['line-prefix_speakAlways'] = True
                break
            else:
                # Not a controlStart, formatChange or text string. Nothing to do.
                break
        # Fill in page number attributes where NVDA expects
        try:
            page = fields[0].field['page-number']
        except KeyError:
            page = None
        if page is not None:
            for field in fields:
                if isinstance(field, textInfos.FieldCommand) and isinstance(
                        field.field, textInfos.FormatField):
                    field.field['page-number'] = page
        # MS Word can sometimes return a higher ancestor in its textRange's children.
        # E.g. a table inside a table header.
        # This does not cause a loop, but does cause information to be doubled
        # Detect these duplicates and remove them from the generated fields.
        seenStarts = set()
        pendingRemoves = []
        index = 0
        for index, field in enumerate(fields):
            if isinstance(field, textInfos.FieldCommand
                          ) and field.command == "controlStart":
                runtimeID = field.field['runtimeID']
                if not runtimeID:
                    continue
                if runtimeID in seenStarts:
                    pendingRemoves.append(field.field)
                else:
                    seenStarts.add(runtimeID)
            elif seenStarts:
                seenStarts.clear()
        index = 0
        while index < len(fields):
            field = fields[index]
            if isinstance(field, textInfos.FieldCommand) and any(
                    x is field.field for x in pendingRemoves):
                del fields[index]
            else:
                index += 1
        return fields
Exemple #20
0
	def _getText(self, withFields, formatConfig=None):
		fields = []
		if self.isCollapsed:
			return fields

		if withFields:
			# Get the initial control fields.
			controlStack = []
			rootObj = self.obj
			obj = self._startObj
			ti = self._start
			cannotBeStart = False
			while obj and obj != rootObj:
				field = self._getControlFieldForObject(obj)
				if field:
					if ti._startOffset == 0:
						if not cannotBeStart:
							field["_startOfNode"] = True
					else:
						# We're not at the start of this object, which also means we're not at the start of any ancestors.
						cannotBeStart = True
					fields.insert(0, textInfos.FieldCommand("controlStart", field))
				controlStack.insert(0, field)
				ti = self._getEmbedding(obj)
				if not ti:
					log.debugWarning(
						"_getEmbedding returned None while getting initial fields. "
						"Object probably dead."
					)
					return []
				obj = ti.obj
		else:
			controlStack = None

		# Get the fields for start.
		fields += list(self._iterRecursiveText(self._start, controlStack, formatConfig))
		if not fields:
			# We're not getting anything, so the object must be dead.
			# (We already handled collapsed above.)
			return fields
		obj = self._startObj
		while fields[-1] is not None:
			# The end hasn't yet been reached, which means it isn't a descendant of obj.
			# Therefore, continue from where obj was embedded.
			if withFields:
				try:
					field = controlStack.pop()
				except IndexError:
					# We're trying to walk up past our root. This can happen if a descendant
					# object within the range died, in which case _iterRecursiveText will
					# never reach our end object and thus won't yield None. This means this
					# range is invalid, so just return nothing.
					log.debugWarning("Tried to walk up past the root. Objects probably dead.")
					return []
				if field:
					# This object had a control field.
					field["_endOfNode"] = True
					fields.append(textInfos.FieldCommand("controlEnd", None))
			ti = self._getEmbedding(obj)
			if not ti:
				log.debugWarning(
					"_getEmbedding returned None while ascending to get more text. "
					"Object probably dead."
				)
				return []
			obj = ti.obj
			if ti.move(textInfos.UNIT_OFFSET, 1) == 0:
				# There's no more text in this object.
				continue
			ti.setEndPoint(self._makeRawTextInfo(obj, textInfos.POSITION_ALL), "endToEnd")
			fields.extend(self._iterRecursiveText(ti, controlStack, formatConfig))
		del fields[-1]

		if withFields:
			# Determine whether the range covers the end of any ancestors of endObj.
			obj = self._endObj
			ti = self._end
			while obj and obj != rootObj:
				field = controlStack.pop()
				if field:
					fields.append(textInfos.FieldCommand("controlEnd", None))
				if ti.compareEndPoints(self._makeRawTextInfo(obj, textInfos.POSITION_ALL), "endToEnd") == 0:
					if field:
						field["_endOfNode"] = True
				else:
					# We're not at the end of this object, which also means we're not at the end of any ancestors.
					break
				ti = self._getEmbedding(obj)
				obj = ti.obj

		return fields