Some test text!
Web / Guides / Header
There are a number of ways you may want to customize the header. To name a few:
The WebViewer UI provides a flexible API to easily handle each of these cases and more.
To learn about structure and different types of items, you can start reading from header composition and header items . Otherwise, you can jump straight to examples and relevant APIs .
There are two main types of headers. The first one is the top header. This header defaults to navigation tools, opening sub-menus and panels. Also in the center you will see a collection of toolbar group labels, called ribbons.
Selecting a toolbar group other than View
will reveal the tools header. Each tools header has a different combination of tools. They can be modified individually.
Each header is represented by an array of header items. The array can be edited to add/remove/reorder items. The API, setHeaderItems, will provide the top header object as a function argument by default. To access a tools header please see the examples in the API link.
New in version 7.0, are the ribbons in the top header. This is a collection of toolbar groups. Each group reveals a second tools header that contain a different set of tools. There are various API calls that can be use to customize the ribbons.
To set a default to toolbar to load on startup, call setToolbarGroup. Alternatively, it can be called at any time to change the current toolbar group.
WebViewer(...)
.then(instance => {
// Sets the current toolbar group
instance.UI.setToolbarGroup('toolbarGroup-Insert');
});
If you want to hide a specific toolbar group, like other DOM elements, they can be hidden by using disableElements.
WebViewer(...)
.then(instance => {
// hide the Shapes, Edit and Insert toolbar groups.
instance.UI.disableElements(['toolbarGroup-Shapes']);
instance.UI.disableElements(['toolbarGroup-Edit']);
instance.UI.disableElements(['toolbarGroup-Insert']);
});
Alternatively, you may only want to use one toolbar group and hide the ribbons altogether. This may be done with code similar to the following:
WebViewer(...)
.then(instance => {
// hide the ribbons
instance.UI.disableElements(['ribbons']);
// set the default toolbar group to the Shapes group
instance.UI.setToolbarGroup('toolbarGroup-Shapes');
});
Lastly, you can move specific tools from one group to another. Below is an example of moving the line tool from the Shapes
toolbar group to the Annotate
toolbar group.
// Moving the line tool from the 'Shapes' toolbar group to the 'Annotate' toolbar group
WebViewer(...)
.then(function(instance) {
instance.UI.setHeaderItems(function(header) {
header.getHeader('toolbarGroup-Annotate').push({
type: 'toolGroupButton',
toolGroup: 'lineTools',
dataElement: 'lineToolGroupButton',
title: 'annotation.line',
});
header.getHeader('toolbarGroup-Shapes').delete(6);
});
});
Header items are objects with certain properties. If you wish to add a header item, it is important for you to understand what type it is and what properties should be used.
ActionButton triggers an action. The button has no active state.
Properties
actionButton
..svg
from the WebViewerassets/icons/
folder (i.e. calibrate
to use
calibrate.svg
).
data-element
value of the button element. It can be used to disable/enable the element.desktop
, tablet
and mobile
.Example
const newActionButton = {
type: 'actionButton',
img: 'path/to/image',
onClick: () => {
alert('Hello world!');
},
dataElement: 'alertButton',
hidden: [ 'mobile' ]
};
StatefulButton is a customizable button. You can decide how many states it has, what state is active and when to update the state.
Properties
statefulButton
..svg
from the WebViewer assets/icons/
folder (i.e. calibrate
to use calibrate.svg
).data-element
value of the button element. It can be used to disable/enable the element.desktop
, tablet
and mobile
.Example
A stateful button that shows the count. And when you click it, it will increment the counter by 1.
const countButton = {
type: 'statefulButton',
initialState: 'Count',
states: {
Count: {
number: 1,
getContent: activeState => {
return activeState.number;
},
onClick: (update, activeState) => {
activeState.number += 1;
update();
}
}
},
dataElement: 'countButton'
};
A stateful button that shows the current page number. And when you click it, the document will go to next page. If you are already at the last page, the document will go to the first page.
const nextPageButton = {
type: 'statefulButton',
initialState: 'Page',
states: {
Page: {
// Checkout https://docs.apryse.com/api/web/WebViewerInstance.html to see more APIs related with viewerInstance
getContent: instance.UI.getCurrentPageNumber,
onClick: () => {
const currentPage = instance.UI.getCurrentPageNumber();
const totalPages = instance.UI.getPageCount();
const atLastPage = currentPage === totalPages;
if (atLastPage) {
instance.UI.goToFirstPage();
} else {
instance.UI.goToNextPage();
}
}
}
},
mount: update => {
// Checkout https://docs.apryse.com/api/web/Core.DocumentViewer.html to see more APIs and events with docViewer
// We want to update this button when page number changes so it can have the correct content;
instance.Core.documentViewer.addEventListener('pageNumberUpdated.nextPageButton', update);
},
unmount: () => {
instance.Core.documentViewer.removeEventListener('pageNumberUpdated.nextPageButton');
},
dataElement: 'nextPageButton'
};
A stateful button that changes the fit mode of the document.
This button is in our built-in UI, checkout it out at https://showcase.apryse.com.
const fitButton = {
type: 'statefulButton',
initialState: 'FitWidth',
states: {
FitWidth: {
img: 'path/to/fitWidth/image',
onClick: () => {
instance.UI.setFitMode(instance.UI.FitMode.FitWidth);
},
},
FitPage: {
img: 'path/to/fitPage/image',
onClick: () => {
instance.UI.setFitMode(instance.UI.FitMode.FitPage);
},
}
},
mount: update => {
const fitModeToState = fitMode => {
// the returned state should be the opposite of the new current state
// as the opposite state is what we want to switch to when the button
// is clicked next
if (fitMode === instance.Core.documentViewer.FitMode.FitPage) {
return 'FitWidth';
} else if (fitMode === instance.Core.documentViewer.FitMode.FitWidth) {
return 'FitPage';
}
};
instance.Core.documentViewer.addEventListener('fitModeUpdated.fitbutton', fitMode => {
update(fitModeToState(fitMode));
});
},
unmount: () => {
instance.Core.documentViewer.removeEventListener('fitModeUpdated.fitbutton');
},
dataElement: 'fitButton',
hidden: ['mobile']
};
ToggleElementButton opens/closes a UI element. The button is in an active state when the UI element is open.
Properties
toggleElementButton
..svg
from the WebViewer assets/icons/
folder (i.e. calibrate
to use calibrate.svg
).data-element
attribute value of the UI element to be opened/closed.data-element
value of the button element. It can be used to disable/enable the element.desktop
, tablet
and mobile
.Example
const newToggleElementButton = {
type: 'toggleElementButton',
img: 'path/to/image',
element: 'leftPanel',
dataElement: 'leftPanelButton',
hidden: [ 'mobile' ]
};
ToolButton activates a WebViewer tool. The button is in an active state when the tool is activated. To learn more about customizing annotation tools and tool buttons, see customizing tools .
Properties
toolButton
.desktop
, tablet
and mobile
.Example
const newToolButton = {
type: 'toolButton',
toolName: 'AnnotationCreateFreeText',
hidden: [ 'mobile' ]
}
A ToolGroupButton
shows the tools available to that tool group. Unless the img
option is set, the button displays the image of one of the group members. The button is in an active state when any tool in the group is active. To learn more about customizing annotation tools and tool buttons, see customizing tools .
Properties
freeHandTools
, textTools
, shapeTools
and miscTools
..svg
from the WebViewer assets/icons/
folder (i.e. calibrate
to use calibrate.svg
).data-element
value of the button element. It can be used to disable/enable the element.desktop
, tablet
and mobile
.Example
const newToolGroupButton = {
type: 'toolGroupButton',
toolGroup: 'shapeTools',
dataElement: 'shapeToolGroupButton',
hidden: [ 'mobile' ]
};
CustomElement takes a render function and renders DOM elements or React components. You may want to use this when the buttons above don't suffice.
Properties
desktop
, tablet
and mobile
.Example
const renderSlider = () => {
const slider = document.createElement('input');
slider.type = 'range';
slider.oninput = () => {
// Do something
};
return slider;
}
const newCustomElement = {
type: 'customElement',
render: renderSlider
};
In React jsx:
const Slider = () => {
return (
<input
type="range"
onInput={() => { /* Do something */ }}
>
</input>
);
}
const newCustomElement = {
type: 'customElement',
render: () => <Slider />
};
Spacer is just a div
with flex: 1
to occupy any remaining space. It is used to push the buttons to each side on the default header.
Properties
desktop
, tablet
and mobile
.Example
const newSpacer = {
type: 'spacer',
hidden: [ 'mobile' ]
};
Divider renders a vertical bar with some margin to separate item groups.
Properties
desktop
, tablet
and mobile
.Example
const newDivider = {
type: 'divider',
hidden: [ 'mobile' ]
};
WebViewer(...)
.then(instance => {
instance.UI.setHeaderItems(header => {
header.push({
type: 'actionButton',
img: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>',
onClick: () => {
// save the annotations
}
});
});
});
After you added a custom save button, here is a code sample you can add inside of onClick function for saving a PDF.
WebViewer(...)
.then(instance => {
instance.UI.setHeaderItems(function(header) {
// get the tools overlay
const toolsOverlay = header.getHeader('toolbarGroup-Annotate').get('toolsOverlay');
header.getHeader('toolbarGroup-Annotate').delete('toolsOverlay');
// add the line tool to the top header
header.getHeader('default').push({
type: 'toolGroupButton',
toolGroup: 'lineTools',
dataElement: 'lineToolGroupButton',
title: 'annotation.line',
});
// add the tools overlay to the top header
header.push(toolsOverlay);
});
// hide the ribbons and second header
instance.UI.disableElements(['ribbons']);
instance.UI.disableElements(['toolsHeader']);
});
WebViewer(...)
.then(instance => {
const { documentViewer } = instance.Core;
const document = instance.UI.iframeWindow.document;
instance.UI.setHeaderItems(header => {
const parent = documentViewer.getScrollViewElement().parentElement;
const menu = document.createElement('div');
menu.classList.add('Overlay');
menu.classList.add('FlyoutMenu');
menu.style.padding = '1em';
const downloadBtn = document.createElement('button');
downloadBtn.textContent = 'Download';
downloadBtn.onclick = () => {
// Download
};
menu.appendChild(downloadBtn);
let isMenuOpen = false;
const renderCustomMenu = () => {
const menuBtn = document.createElement('button');
menuBtn.textContent = 'My Menu';
menuBtn.onclick = () => {
if (isMenuOpen) {
parent.removeChild(menu);
} else {
menu.style.left = `${document.body.clientWidth - (menuBtn.clientWidth + 40)}px`;
menu.style.right = 'auto';
menu.style.top = '40px';
parent.appendChild(menu);
}
isMenuOpen = !isMenuOpen;
};
return menuBtn;
};
const newCustomElement = {
type: 'customElement',
render: renderCustomMenu,
};
header.push(newCustomElement);
});
});
WebViewer(...)
.then(instance => {
const { docViewer } = instance;
const document = instance.iframeWindow.document;
instance.UI.setHeaderItems(header => {
const parent = docViewer.getScrollViewElement().parentElement;
const menu = document.createElement('div');
menu.classList.add('Overlay');
menu.classList.add('FlyoutMenu');
menu.style.padding = '1em';
const downloadBtn = document.createElement('button');
downloadBtn.textContent = 'Download';
downloadBtn.onclick = () => {
// Download
};
menu.appendChild(downloadBtn);
let isMenuOpen = false;
const renderCustomMenu = () => {
const menuBtn = document.createElement('button');
menuBtn.textContent = 'My Menu';
menuBtn.onclick = () => {
if (isMenuOpen) {
parent.removeChild(menu);
} else {
menu.style.left = `${document.body.clientWidth - (menuBtn.clientWidth + 40)}px`;
menu.style.right = 'auto';
menu.style.top = '40px';
parent.appendChild(menu);
}
isMenuOpen = !isMenuOpen;
};
return menuBtn;
};
const newCustomElement = {
type: 'customElement',
render: renderCustomMenu,
};
header.push(newCustomElement);
});
});
WebViewer(...)
.then(instance => {
instance.UI.setHeaderItems(header => {
const items = header.getItems().slice(9, -3);
header.update(items);
});
});
WebViewer(...)
.then(instance => {
// remove the tools from existing group
instance.UI.updateTool('AnnotationCreateTextHighlight', { buttonGroup: '' });
instance.UI.updateTool('AnnotationCreateTextUnderline', { buttonGroup: '' });
instance.UI.updateTool('AnnotationCreateTextSquiggly', { buttonGroup: '' });
instance.UI.updateTool('AnnotationCreateTextStrikeout', { buttonGroup: '' });
// delete default tool buttons and add new ones in desired order
instance.UI.setHeaderItems(header => {
const items = header.getItems();
items.splice(10, 9,
{
type: 'toolButton',
toolName: 'AnnotationCreateTextStrikeout'
},
{
type: 'toolButton',
toolName: 'AnnotationCreateTextSquiggly'
},
{
type: 'toolButton',
toolName: 'AnnotationCreateTextUnderline'
},
{
type: 'toolButton',
toolName: 'AnnotationCreateTextHighlight'
}
);
header.update(items);
});
});
WebViewer(...)
.then(instance => {
instance.UI.setHeaderItems(header => {
header.delete(9);
header.unshift({
type: 'customElement',
render: () => {
const logo = document.createElement('img');
logo.src = 'https://www.pdftron.com/downloads/logo.svg';
logo.style.width = '200px';
logo.style.marginLeft = '10px';
logo.style.cursor = 'pointer';
logo.onclick = () => {
window.open('https://www.pdftron.com', '_blank');
}
return logo;
}
}, {
type: 'spacer'
});
});
});
To add/remove/re-order header items, you can use the following API:
For ToolButton
, make sure you register/unregister the tool using:
Trial setup questions? Ask experts on Discord
Need other help? Contact Support
Pricing or product questions? Contact Sales