Search code examples
pdftron

How to rotate individual pages in a pdf in web viewer


I am using web viewer and want to rotate individual pages and update them in the database. Right now I am able to rotate the whole pdf only. I am following this doc https://www.pdftron.com/documentation/web/guides/manipulation/rotate/ but not able to understand much

export default function PdfTron(props: any): ReactElement {

const viewer = useRef<HTMLDivElement>(null);
const {DrawingLibDetailsState, DrawingLibDetailsDispatch}: any = useContext(DrawingLibDetailsContext);
const [newInstance, setNewInstance] = useState<any>(null);
const [currentPage, setCurrentPage] = useState<any>(null);
const {dispatch, state }:any = useContext(stateContext);
//console.log("currentPage in state",currentPage)

useEffect(() => {
  WebViewer(
    {
      path: '/webviewer/lib',
      licenseKey: process.env["REACT_APP_PDFTRON_LICENSE_KEY"],
      initialDoc: '',
      filename: 'drawings',
      extension: "pdf",
      isReadOnly: true,
      fullAPI: true,
      disabledElements: [
        // 'leftPanelButton',
      // //   'selectToolButton',
      //   'stickyToolButton',
      //   'toggleNotesButton',
      ]
    },
    viewer.current as HTMLDivElement,
  ).then((instance: any) => {
    setNewInstance(instance)
      // you can now call WebViewer APIs here...
  });
}, []);

useEffect(() => {
  if(DrawingLibDetailsState?.parsedFileUrl?.url && newInstance ){
    const s3Key = DrawingLibDetailsState?.parsedFileUrl?.s3Key;
    const pageNum = s3Key.split('/')[s3Key.split('/').length-1].split('.')[0];
    const fileName = DrawingLibDetailsState?.drawingLibDetails[0]?.fileName?.replace(".pdf", "");
    const downloadingFileName = `page${pageNum}_${fileName}`;

    newInstance.loadDocument(DrawingLibDetailsState?.parsedFileUrl?.url, {extension: "pdf", 
    filename: downloadingFileName ? downloadingFileName : 'drawing',})

    const { documentViewer } = newInstance.Core;
    const pageRotation = newInstance.Core.PageRotation;
    const clickDocument =newInstance.Core.DocumentViewer.Click;
    const pageNumber = newInstance.Core.pageNum;

    //get page rotation from the PDF
    documentViewer.addEventListener('rotationUpdated', (rotation: number) => {
      updateRotation(rotation)
    })
    


    // trigger an event after the document loaded
    documentViewer.addEventListener('documentLoaded', async() => {
      const doc = documentViewer.getDocument();
      const rotation = DrawingLibDetailsState?.drawingLibDetails[0]?.sheetsReviewed?.pdfRotation ? 
                        DrawingLibDetailsState?.drawingLibDetails[0]?.sheetsReviewed?.pdfRotation : 0
      documentViewer.setRotation(rotation)
    })

    documentViewer.on('pageNumberUpdated', () => {
      DrawingLibDetailsDispatch(setDrawingPageNumber(0));
    })

  }
}, [DrawingLibDetailsState?.parsedFileUrl?.url, newInstance]);

useEffect(() => {
  if(DrawingLibDetailsState?.drawingPageNum && newInstance ){
    const { documentViewer, PDFNet } = newInstance.Core;
      PDFNet.initialize()
      documentViewer.addEventListener('documentLoaded',async () => {
        await PDFNet.initialize()
        const pdfDoc = documentViewer.getDocument();
        const doc = await pdfDoc.getPDFDoc();
        newInstance.UI.pageManipulationOverlay.add([
          {
            type: 'customPageOperation',
            header: 'Custom options',
            dataElement: 'customPageOperations',
            operations: [
              {
                title: 'Alert me',
                img: '/path-to-image',
                onClick: (selectedPageNumbers:any) => {
                  alert(`Selected thumbnail pages: ${selectedPageNumbers}`);
                },
                dataElement: 'customPageOperationButton',
              },
            ],
          },
          { type: 'divider' },
        ]);
        documentViewer.setCurrentPage(DrawingLibDetailsState?.drawingPageNum, true);
      });
      documentViewer.setCurrentPage(DrawingLibDetailsState?.drawingPageNum, true);
  }
}, [DrawingLibDetailsState?.drawingPageNum]);

useEffect(() => {
  if(props?.drawingSheetsDetails?.fileSize){
      fetchSheetUrl(props?.drawingSheetsDetails)
  }
}, [props?.drawingSheetsDetails]);

const fetchSheetUrl = (file: any) => {
    const payload = [{
        fileName: file.fileName,
        key: file.sourceKey,
        expiresIn: 100000000,
        // processed: true
    }]; 

    getSheetUrl(payload);
}

const getSheetUrl = async (payload: any) => {
    try {
        dispatch(setIsLoading(true));
        const fileUploadResponse = await postApi('V1/S3/downloadLink', payload);
        if(fileUploadResponse.success){
            const fileData = {
                s3Key: payload[0].key,
                url: fileUploadResponse.success[0].url
            }
            DrawingLibDetailsDispatch(setParsedFileUrl(fileData));
        }
        dispatch(setIsLoading(false));
    } catch (error) {   
        Notification.sendNotification(error, AlertTypes.warn);
        dispatch(setIsLoading(false));
    }
}

const updateRotation = (rotation: number) => {
  props.updateRotation(rotation)
}

return (
    <>
        <div className="webviewer" ref={viewer}></div>
    </>
)
}

Solution

  • In WebViewer 8.0 you would need to enable the left panel by default when the document is loaded, and then use event delegation on left panel to watch for button clicks on the single page rotation buttons.

    const { documentViewer } = instance.Core
    documentViewer.addEventListener('documentLoaded',()=>{
      let panelElement = instance.docViewer.getScrollViewElement().closest('#app').querySelector('[data-element="thumbnailsPanel"]');
      if (!parentElement) {
        instance.UI.toggleElementVisibility('leftPanel');
        panelElement = instance.docViewer.getScrollViewElement().closest('#app').querySelector('[data-element="thumbnailsPanel"]');
      }
      panelElement.addEventListener('click', (e) => {
        if (e.target.dataset?.element === 'thumbRotateClockwise' || e.target.dataset?.element === 'thumbRotateCounterClockwise') {
          // The single page rotations are performed asychronously and there are no events firings in 8.0, so we have to manually add a delay before the page finishes rotating itself.
          setTimeout(() => {
            const pageNumber = parseInt(e.target.parentElement.previousSibling.textContent);
            const rotation = instance.docViewer.getDocument().getPageRotation(pageNumber);
            console.log('page ', pageNumber, ' self rotation is ', rotation);
          }, 500);
        }
      });
    })
    

    If you have the option to upgrade to the latest WebViewer, you can listen to the ‘pagesUpdated’ event on documentViewer and the code becomes shorter & cleaner:

    const { documentViewer } = instance.Core
    documentViewer.addEventListener('pagesUpdated',(changes)=>{
        changes.contentChanged.forEach(pageNumber=>{
            const rotation = documentViewer.getDocument().getPageRotation(pageNumber)
            console.log('page ', pageNumber, ' self rotation is ', rotation);
        })
    })
    

    For both situations, when you load the document back, you can use documentViewer.getDocument().rotatePages to rotate to your saved rotations.

    assuming we have the saved page rotations data structured like this

    const rotationData = [
      {pageNumber: 1, rotation: 180},
      {pageNumber: 3, rotation: 90},
      {pageNumber: 4, rotation: 270},
    ]
    We can use the following code to rotate our individual pages back:
    
    const { documentViewer } = instance.Core
    documentViewer.addEventListener('documentLoaded',()=>{
      rotationData.forEach(page=>{
        const originalRotation = documentViewer.getDocument().getPageRotation(page.pageNumber)
        if (originalRotation !== page.rotation) {
          documentViewer.getDocument().rotatePages([page.pageNumber], (page.rotation-originalRotation)/90);
        }
      })
    })