I'd use a template matching the top
attribute itself, from where you can refer to the page number as ../../@number
(in XPath an attribute node is not a child of its containing element, but the containing element is the parent of the attribute node):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
</xsl:template>
<xsl:template match="pdf2xml/page">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="*">
<xsl:sort select="@top" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="text/@top">
<xsl:attribute name="top">
<xsl:value-of select=". + (../../@number * 5000)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
You might prefer to make things more explicit by using ancestor::page[1]/@number
instead of ../../@number
to make it clearer to future readers of the code that it's the nearest containing page
you're trying to reach.