diff --git a/src/Page.jsx b/src/Page.jsx index 8424469a8..af4cf5bca 100644 --- a/src/Page.jsx +++ b/src/Page.jsx @@ -81,6 +81,7 @@ export class PageInternal extends PureComponent { onGetAnnotationsSuccess, onGetTextError, onGetTextSuccess, + useObserverAlignText, onRenderAnnotationLayerError, onRenderAnnotationLayerSuccess, onRenderError, @@ -96,6 +97,7 @@ export class PageInternal extends PureComponent { onGetAnnotationsSuccess, onGetTextError, onGetTextSuccess, + useObserverAlignText, onRenderAnnotationLayerError, onRenderAnnotationLayerSuccess, onRenderError, @@ -397,6 +399,7 @@ PageInternal.propTypes = { rotate: isRotate, scale: PropTypes.number, unregisterPage: PropTypes.func, + useObserverAlignText : PropTypes.bool, width: PropTypes.number, }; diff --git a/src/Page/TextLayer.jsx b/src/Page/TextLayer.jsx index 3a216639b..6a5b43835 100644 --- a/src/Page/TextLayer.jsx +++ b/src/Page/TextLayer.jsx @@ -17,6 +17,13 @@ export class TextLayerInternal extends PureComponent { textItems: null, }; + constructor(props) { + super(props) + + this.refTextItems = React.createRef() + this.countLoadedItem = 0 + } + componentDidMount() { const { page } = this.props; @@ -52,13 +59,56 @@ export class TextLayerInternal extends PureComponent { }); }; + observer = new IntersectionObserver((entries) => { + const { sideways } = this; + + let width = 0; + for (const entry of entries) { + width = entry.boundingClientRect[sideways ? 'height' : 'width']; + if(width > 0){ + this.refTextItems.current[entry.target.i].updatedWidth(width).alignTextItem() + } + } + this.observer.disconnect(); + }); + onLoadSuccess = () => { - const { onGetTextSuccess } = this.props; + const { onGetTextSuccess, useObserverAlignText} = this.props; const { textItems } = this.state; + if(useObserverAlignText){ + for(let i = 0; i < this.refTextItems.current.length; i++){ + let entry = this.refTextItems.current[i] + if(entry.props.str.length > 0){ + entry.item.i = i + this.observer.observe(entry.item) + } + } + } if (onGetTextSuccess) onGetTextSuccess(textItems); }; + onLoadSuccessAlignTextItem = async() => { + if(this.countLoadedItem < this.refTextItems.current.length){ + this.countLoadedItem++; + } + if(this.refTextItems.current.length === this.countLoadedItem){ + let indexUpdate = []; + requestAnimationFrame(() => { + for(let i = 0; i < this.refTextItems.current.length; i++){ + if(this.refTextItems.current[i].updateElementWidth()){ + indexUpdate.push(i); + } + } + if(indexUpdate.length > 0){ + for(let i = 0; i < indexUpdate.length; i++){ + this.refTextItems.current[indexUpdate[i]].alignTextItem(); + } + } + }); + } + } + onLoadError = (error) => { this.setState({ textItems: false }); @@ -84,19 +134,30 @@ export class TextLayerInternal extends PureComponent { return rotate - page.rotate; } + get sideways() { + const { rotate } = this; + return rotate % 180 !== 0; + } + renderTextItems() { const { textItems } = this.state; if (!textItems) { + this.refTextItems.current = [] return null; } + let i = -1 + this.countLoadedItem = 0 + this.refTextItems.current = new Array(textItems.length) return textItems.map((textItem, itemIndex) => ( this.refTextItems.current[++i] = ref} // eslint-disable-next-line react/no-array-index-key key={itemIndex} itemIndex={itemIndex} {...textItem} + onLoadSuccessAlignTextItem={!this.props.useObserverAlignText ? this.onLoadSuccessAlignTextItem : function(){ return }} /> )); } diff --git a/src/Page/TextLayerItem.jsx b/src/Page/TextLayerItem.jsx index 01970f229..be8b1f563 100644 --- a/src/Page/TextLayerItem.jsx +++ b/src/Page/TextLayerItem.jsx @@ -7,11 +7,11 @@ import { isPage, isRotate } from '../shared/propTypes'; export class TextLayerItemInternal extends PureComponent { componentDidMount() { - this.alignTextItem(); + this.initTextItem(); } componentDidUpdate() { - this.alignTextItem(); + this.initTextItem(); } get unrotatedViewport() { @@ -71,7 +71,70 @@ export class TextLayerItemInternal extends PureComponent { }); } - alignTextItem() { + initTextItem = () => { + const element = this.item; + + if (!element) { + this.props.onLoadSuccessAlignTextItem() + return; + } + + if(!this.fontFamily && this.props.str.length > 0){ + this.fontFamily = element.style.fontFamily + this.loadTextItem() + } else { + this.props.onLoadSuccessAlignTextItem() + } + } + + loadTextItem = async() => { + const element = this.item; + const { fontName } = this.props; + + let fontData = await this.getFontData(fontName) + const fallbackFontName = fontData ? fontData.fallbackName : 'sans-serif'; + + this.fontFamily = `${fontName}, ${fallbackFontName}`; + this.ascent = fontData ? fontData.ascent : 0; + if(this.fontFamily !== element.style.fontFamily){ + element.style.fontFamily = this.fontFamily + } + this.props.onLoadSuccessAlignTextItem() + } + + updateElementWidth = () => { + const element = this.item; + if(element){ + let actualWidth = this.getElementWidth(element); + + if(this.actualWidth !== actualWidth && actualWidth > 0){ + this.actualWidth = actualWidth + return true + } + } + return false + } + + updatedWidth = (width) => { + this.actualWidth = width + return this + } + + alignTextItem = async() => { + const element = this.item; + const { scale, width } = this.props; + + const targetWidth = width * scale; + let transform = `scaleX(${targetWidth / this.actualWidth})`; + + if (this.ascent) { + transform += ` translateY(${(1 - this.ascent) * 100}%)`; + } + element.style.transform = transform; + element.style.WebkitTransform = transform; + } + + /* alignTextItem() { const element = this.item; if (!element) { @@ -101,7 +164,7 @@ export class TextLayerItemInternal extends PureComponent { element.style.transform = transform; element.style.WebkitTransform = transform; }); - } + }*/ getElementWidth = (element) => { const { sideways } = this; @@ -109,7 +172,7 @@ export class TextLayerItemInternal extends PureComponent { }; render() { - const { fontSize, top, left } = this; + const { fontName, fontSize, top, left } = this; const { customTextRenderer, scale, str: text } = this.props; return ( @@ -119,7 +182,7 @@ export class TextLayerItemInternal extends PureComponent { }} style={{ height: '1em', - fontFamily: 'sans-serif', + fontFamily: `${fontName}, sans-serif`, fontSize: `${fontSize * scale}px`, position: 'absolute', top: `${top * scale}px`, @@ -140,6 +203,7 @@ TextLayerItemInternal.propTypes = { fontName: PropTypes.string.isRequired, itemIndex: PropTypes.number.isRequired, page: isPage.isRequired, + onLoadSuccessAlignTextItem: PropTypes.func.isRequired, rotate: isRotate, scale: PropTypes.number, str: PropTypes.string.isRequired, @@ -147,10 +211,10 @@ TextLayerItemInternal.propTypes = { width: PropTypes.number.isRequired, }; -export default function TextLayerItem(props) { - return ( - - {(context) => } - - ); -} +const TextLayerItem = React.forwardRef((props, ref) => ( + + {(context) => } + + )); + +export default TextLayerItem \ No newline at end of file