diff --git a/index.d.ts b/index.d.ts index d7ad6c70..fe75372d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -71,6 +71,7 @@ export interface PdfProps { fitPolicy?: 0 | 1 | 2, trustAllCerts?: boolean, singlePage?: boolean, + transformFile?: boolean, onLoadProgress?: (percent: number,) => void, onLoadComplete?: (numberOfPages: number, path: string, size: {height: number, width: number}, tableContents?: TableContent[]) => void, onPageChanged?: (page: number, numberOfPages: number) => void, diff --git a/index.js b/index.js index f807178a..dab7cd2f 100644 --- a/index.js +++ b/index.js @@ -57,6 +57,7 @@ export default class Pdf extends Component { fitPolicy: PropTypes.number, trustAllCerts: PropTypes.bool, singlePage: PropTypes.bool, + transformFile: PropTypes.bool, onLoadComplete: PropTypes.func, onPageChanged: PropTypes.func, onError: PropTypes.func, @@ -95,6 +96,7 @@ export default class Pdf extends Component { trustAllCerts: true, usePDFKit: true, singlePage: false, + transformFile: false, onLoadProgress: (percent) => { }, onLoadComplete: (numberOfPages, path) => { @@ -124,7 +126,7 @@ export default class Pdf extends Component { }; this.lastRNBFTask = null; - + this.lastViewFile = null; } componentDidUpdate(prevProps) { @@ -136,10 +138,12 @@ export default class Pdf extends Component { // if has download task, then cancel it. if (this.lastRNBFTask && this.lastRNBFTask.cancel) { this.lastRNBFTask.cancel(err => { + this._cleanupViewFile(); this._loadFromSource(this.props.source); }); this.lastRNBFTask = null; } else { + this._cleanupViewFile(); this._loadFromSource(this.props.source); } } @@ -158,8 +162,39 @@ export default class Pdf extends Component { this.lastRNBFTask = null; } + if (!this.props.cache) { + if (this.props.transformFile) { + // this.state.path is the .view file; unlink the original pre-transformed file. + // The .view file is cleaned up by _cleanupViewFile below. + if (this.lastPreTransformedPath) { + this._unlinkFile(this.lastPreTransformedPath); + } + } else { + this._unlinkFile(this.state.path); + } + } + + this._cleanupViewFile(); + } + _cleanupViewFile = () => { + if (this.lastViewFile) { + this._unlinkFile(this.lastViewFile); + this.lastViewFile = null; + } + }; + + _transformToViewFile = async (preTransformedPath) => { + const viewFile = preTransformedPath + '.view'; + this._unlinkFile(viewFile); + const base64 = await ReactNativeBlobUtil.fs.readFileWithTransform(preTransformedPath, 'base64'); + await ReactNativeBlobUtil.fs.writeFile(viewFile, base64, 'base64'); + this.lastViewFile = viewFile; + this.lastPreTransformedPath = preTransformedPath; + return viewFile; + }; + _loadFromSource = (newSource) => { const source = Image.resolveAssetSource(newSource) || {}; @@ -175,10 +210,17 @@ export default class Pdf extends Component { if (source.cache) { ReactNativeBlobUtil.fs .stat(cacheFile) - .then(stats => { + .then(async stats => { if (!Boolean(source.expiration) || (source.expiration * 1000 + stats.lastModified) > (new Date().getTime())) { - if (this._mounted) { - this.setState({path: cacheFile, isDownloaded: true}); + try { + const finalPath = this.props.transformFile + ? await this._transformToViewFile(cacheFile) + : cacheFile; + if (this._mounted) { + this.setState({path: finalPath, isDownloaded: true}); + } + } catch (e) { + this._onError(e); } } else { // cache expirated then reload it @@ -239,10 +281,25 @@ export default class Pdf extends Component { }); } else { if (this._mounted) { - this.setState({ - path: decodeURIComponent(uri.replace(/file:\/\//i, '')), - isDownloaded: true, - }); + const localPath = decodeURIComponent(uri.replace(/file:\/\//i, '')); + if (this.props.transformFile) { + try { + const viewFile = await this._transformToViewFile(localPath); + if (this._mounted) { + this.setState({ + path: viewFile, + isDownloaded: true, + }); + } + } catch (e) { + this._onError(e); + } + } else { + this.setState({ + path: localPath, + isDownloaded: true, + }); + } } } } else { @@ -270,6 +327,7 @@ export default class Pdf extends Component { // response data will be saved to this path if it has access right. path: tempCacheFile, trusty: this.props.trustAllCerts, + transformFile: !!this.props.transformFile, }) .fetch( source.method ? source.method : 'GET', @@ -293,7 +351,7 @@ export default class Pdf extends Component { this.lastRNBFTask = null; - if (res && res.respInfo && res.respInfo.headers && !res.respInfo.headers["Content-Encoding"] && !res.respInfo.headers["Transfer-Encoding"] && res.respInfo.headers["Content-Length"]) { + if (!this.props.transformFile && res && res.respInfo && res.respInfo.headers && !res.respInfo.headers["Content-Encoding"] && !res.respInfo.headers["Transfer-Encoding"] && res.respInfo.headers["Content-Length"]) { const expectedContentLength = res.respInfo.headers["Content-Length"]; let actualContentLength; @@ -317,11 +375,19 @@ export default class Pdf extends Component { this._unlinkFile(cacheFile); ReactNativeBlobUtil.fs .cp(tempCacheFile, cacheFile) - .then(() => { - if (this._mounted) { - this.setState({path: cacheFile, isDownloaded: true, progress: 1}); + .then(async () => { + try { + const finalPath = this.props.transformFile + ? await this._transformToViewFile(cacheFile) + : cacheFile; + if (this._mounted) { + this.setState({path: finalPath, isDownloaded: true, progress: 1}); + } + this._unlinkFile(tempCacheFile); + } catch (e) { + this._unlinkFile(tempCacheFile); + this._onError(e); } - this._unlinkFile(tempCacheFile); }) .catch(async (error) => { throw error; @@ -365,7 +431,7 @@ export default class Pdf extends Component { page: pageNumber }); } - + } _onChange = (event) => {