Merge pull request #9593 from Snuffleupagus/pageSize-orientation
Improve the pageSize entry in the document properties dialog, by also displaying page names and orientation
This commit is contained in:
commit
a8e9f6cc29
@ -90,12 +90,22 @@ document_properties_producer=PDF Producer:
|
||||
document_properties_version=PDF Version:
|
||||
document_properties_page_count=Page Count:
|
||||
document_properties_page_size=Page Size:
|
||||
# LOCALIZATION NOTE (document_properties_page_size_in_2): "{{width}}" and "{{height}}"
|
||||
# will be replaced by the size of the (current) page, in inches.
|
||||
document_properties_page_size_in_2={{width}} × {{height}} in
|
||||
# LOCALIZATION NOTE (document_properties_page_size_mm_2): "{{width}}" and "{{height}}"
|
||||
# will be replaced by the size of the (current) page, in millimeters.
|
||||
document_properties_page_size_mm_2={{width}} × {{height}} mm
|
||||
document_properties_page_size_unit_inches=in
|
||||
document_properties_page_size_unit_millimeters=mm
|
||||
document_properties_page_size_orientation_portrait=portrait
|
||||
document_properties_page_size_orientation_landscape=landscape
|
||||
document_properties_page_size_name_a3=A3
|
||||
document_properties_page_size_name_a4=A4
|
||||
document_properties_page_size_name_letter=Letter
|
||||
document_properties_page_size_name_legal=Legal
|
||||
# LOCALIZATION NOTE (document_properties_page_size_dimension_string):
|
||||
# "{{width}}", "{{height}}", {{unit}}, and {{orientation}} will be replaced by
|
||||
# the size, respectively their unit of measurement and orientation, of the (current) page.
|
||||
document_properties_page_size_dimension_string={{width}} × {{height}} {{unit}} ({{orientation}})
|
||||
# LOCALIZATION NOTE (document_properties_page_size_dimension_name_string):
|
||||
# "{{width}}", "{{height}}", {{unit}}, {{name}}, and {{orientation}} will be replaced by
|
||||
# the size, respectively their unit of measurement, name, and orientation, of the (current) page.
|
||||
document_properties_page_size_dimension_name_string={{width}} × {{height}} {{unit}} ({{name}}, {{orientation}})
|
||||
document_properties_close=Close
|
||||
|
||||
print_progress_message=Preparing document for printing…
|
||||
|
@ -90,12 +90,22 @@ document_properties_producer=PDF-producent:
|
||||
document_properties_version=PDF-version:
|
||||
document_properties_page_count=Sidantal:
|
||||
document_properties_page_size=Sidstorlek:
|
||||
# LOCALIZATION NOTE (document_properties_page_size_in_2): "{{width}}" and "{{height}}"
|
||||
# will be replaced by the size of the (current) page, in inches.
|
||||
document_properties_page_size_in_2={{width}} × {{height}} tum
|
||||
# LOCALIZATION NOTE (document_properties_page_size_mm_2): "{{width}}" and "{{height}}"
|
||||
# will be replaced by the size of the (current) page, in millimeters.
|
||||
document_properties_page_size_mm_2={{width}} × {{height}} mm
|
||||
document_properties_page_size_unit_inches=tum
|
||||
document_properties_page_size_unit_millimeters=mm
|
||||
document_properties_page_size_orientation_portrait=stående
|
||||
document_properties_page_size_orientation_landscape=liggande
|
||||
document_properties_page_size_name_a3=A3
|
||||
document_properties_page_size_name_a4=A4
|
||||
document_properties_page_size_name_letter=Letter
|
||||
document_properties_page_size_name_legal=Legal
|
||||
# LOCALIZATION NOTE (document_properties_page_size_dimension_string):
|
||||
# "{{width}}", "{{height}}", {{unit}}, and {{orientation}} will be replaced by
|
||||
# the size, respectively their unit of measurement and orientation, of the (current) page.
|
||||
document_properties_page_size_dimension_string={{width}} × {{height}} {{unit}} ({{orientation}})
|
||||
# LOCALIZATION NOTE (document_properties_page_size_dimension_name_string):
|
||||
# "{{width}}", "{{height}}", {{unit}}, {{name}}, and {{orientation}} will be replaced by
|
||||
# the size, respectively their unit of measurement, name, and orientation, of the (current) page.
|
||||
document_properties_page_size_dimension_name_string={{width}} × {{height}} {{unit}} ({{name}}, {{orientation}})
|
||||
document_properties_close=Stäng
|
||||
|
||||
print_progress_message=Förbereder sidor för utskrift…
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import {
|
||||
binarySearchFirstItem, EventBus, getPageSizeInches, getPDFFileNameFromURL,
|
||||
isValidRotation, waitOnEventOrTimeout, WaitOnType
|
||||
isPortraitOrientation, isValidRotation, waitOnEventOrTimeout, WaitOnType
|
||||
} from '../../web/ui_utils';
|
||||
import { createObjectURL } from '../../src/shared/util';
|
||||
import isNodeJS from '../../src/shared/is_node';
|
||||
@ -285,6 +285,27 @@ describe('ui_utils', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPortraitOrientation', function() {
|
||||
it('should be portrait orientation', function() {
|
||||
expect(isPortraitOrientation({
|
||||
width: 200,
|
||||
height: 400,
|
||||
})).toEqual(true);
|
||||
|
||||
expect(isPortraitOrientation({
|
||||
width: 500,
|
||||
height: 500,
|
||||
})).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be landscape orientation', function() {
|
||||
expect(isPortraitOrientation({
|
||||
width: 600,
|
||||
height: 300,
|
||||
})).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('waitOnEventOrTimeout', function() {
|
||||
let eventBus;
|
||||
|
||||
|
@ -14,9 +14,10 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, isValidRotation,
|
||||
MAX_AUTO_SCALE, NullL10n, PresentationModeState, RendererType,
|
||||
SCROLLBAR_PADDING, TextLayerMode, UNKNOWN_SCALE, VERTICAL_PADDING, watchScroll
|
||||
CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, isPortraitOrientation,
|
||||
isValidRotation, MAX_AUTO_SCALE, NullL10n, PresentationModeState,
|
||||
RendererType, SCROLLBAR_PADDING, TextLayerMode, UNKNOWN_SCALE,
|
||||
VERTICAL_PADDING, watchScroll
|
||||
} from './ui_utils';
|
||||
import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue';
|
||||
import { AnnotationLayerBuilder } from './annotation_layer_builder';
|
||||
@ -94,10 +95,6 @@ function isSameScale(oldScale, newScale) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isPortraitOrientation(size) {
|
||||
return size.width <= size.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple viewer control to display PDF content/pages.
|
||||
* @implements {IRenderableView}
|
||||
@ -578,11 +575,10 @@ class BaseViewer {
|
||||
scale = Math.min(pageWidthScale, pageHeightScale);
|
||||
break;
|
||||
case 'auto':
|
||||
let isLandscape = (currentPage.width > currentPage.height);
|
||||
// For pages in landscape mode, fit the page height to the viewer
|
||||
// *unless* the page would thus become too wide to fit horizontally.
|
||||
let horizontalScale = isLandscape ?
|
||||
Math.min(pageHeightScale, pageWidthScale) : pageWidthScale;
|
||||
let horizontalScale = isPortraitOrientation(currentPage) ?
|
||||
pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
|
||||
scale = Math.min(MAX_AUTO_SCALE, horizontalScale);
|
||||
break;
|
||||
default:
|
||||
|
@ -145,6 +145,10 @@ class MozL10n {
|
||||
this.mozL10n = mozL10n;
|
||||
}
|
||||
|
||||
getLanguage() {
|
||||
return Promise.resolve(this.mozL10n.getLanguage());
|
||||
}
|
||||
|
||||
getDirection() {
|
||||
return Promise.resolve(this.mozL10n.getDirection());
|
||||
}
|
||||
|
@ -27,6 +27,12 @@ class GenericL10n {
|
||||
});
|
||||
}
|
||||
|
||||
getLanguage() {
|
||||
return this._ready.then((l10n) => {
|
||||
return l10n.getLanguage();
|
||||
});
|
||||
}
|
||||
|
||||
getDirection() {
|
||||
return this._ready.then((l10n) => {
|
||||
return l10n.getDirection();
|
||||
|
@ -160,6 +160,11 @@ class IPDFAnnotationLayerFactory {
|
||||
* @interface
|
||||
*/
|
||||
class IL10n {
|
||||
/**
|
||||
* @returns {Promise<string>} - Resolves to the current locale.
|
||||
*/
|
||||
getLanguage() {}
|
||||
|
||||
/**
|
||||
* @returns {Promise<string>} - Resolves to 'rtl' or 'ltr'.
|
||||
*/
|
||||
|
@ -14,12 +14,34 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
cloneObj, getPageSizeInches, getPDFFileNameFromURL, NullL10n
|
||||
cloneObj, getPageSizeInches, getPDFFileNameFromURL, isPortraitOrientation,
|
||||
NullL10n
|
||||
} from './ui_utils';
|
||||
import { createPromiseCapability } from 'pdfjs-lib';
|
||||
|
||||
const DEFAULT_FIELD_CONTENT = '-';
|
||||
|
||||
// See https://en.wikibooks.org/wiki/Lentis/Conversion_to_the_Metric_Standard_in_the_United_States
|
||||
const NON_METRIC_LOCALES = ['en-us', 'en-lr', 'my'];
|
||||
|
||||
// Should use the format: `width x height`, in portrait orientation.
|
||||
// See https://en.wikipedia.org/wiki/Paper_size
|
||||
const US_PAGE_NAMES = {
|
||||
'8.5x11': 'Letter',
|
||||
'8.5x14': 'Legal',
|
||||
};
|
||||
const METRIC_PAGE_NAMES = {
|
||||
'297x420': 'A3',
|
||||
'210x297': 'A4',
|
||||
};
|
||||
|
||||
function getPageName(size, isPortrait, pageNames) {
|
||||
const width = (isPortrait ? size.width : size.height);
|
||||
const height = (isPortrait ? size.height : size.width);
|
||||
|
||||
return pageNames[`${width}x${height}`];
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PDFDocumentPropertiesOptions
|
||||
* @property {string} overlayName - Name/identifier for the overlay.
|
||||
@ -55,7 +77,15 @@ class PDFDocumentProperties {
|
||||
eventBus.on('pagechanging', (evt) => {
|
||||
this._currentPageNumber = evt.pageNumber;
|
||||
});
|
||||
eventBus.on('rotationchanging', (evt) => {
|
||||
this._pagesRotation = evt.pagesRotation;
|
||||
});
|
||||
}
|
||||
|
||||
this._isNonMetricLocale = true; // The default viewer locale is 'en-us'.
|
||||
l10n.getLanguage().then((locale) => {
|
||||
this._isNonMetricLocale = NON_METRIC_LOCALES.includes(locale);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,11 +104,13 @@ class PDFDocumentProperties {
|
||||
Promise.all([this.overlayManager.open(this.overlayName),
|
||||
this._dataAvailableCapability.promise]).then(() => {
|
||||
const currentPageNumber = this._currentPageNumber;
|
||||
const pagesRotation = this._pagesRotation;
|
||||
|
||||
// If the document properties were previously fetched (for this PDF file),
|
||||
// just update the dialog immediately to avoid redundant lookups.
|
||||
if (this.fieldData &&
|
||||
currentPageNumber === this.fieldData['_currentPageNumber']) {
|
||||
currentPageNumber === this.fieldData['_currentPageNumber'] &&
|
||||
pagesRotation === this.fieldData['_pagesRotation']) {
|
||||
this._updateUI();
|
||||
return;
|
||||
}
|
||||
@ -94,11 +126,12 @@ class PDFDocumentProperties {
|
||||
this._parseDate(info.CreationDate),
|
||||
this._parseDate(info.ModDate),
|
||||
this.pdfDocument.getPage(currentPageNumber).then((pdfPage) => {
|
||||
return this._parsePageSize(getPageSizeInches(pdfPage));
|
||||
return this._parsePageSize(getPageSizeInches(pdfPage),
|
||||
pagesRotation);
|
||||
}),
|
||||
]);
|
||||
}).then(([info, metadata, fileName, fileSize, creationDate, modDate,
|
||||
pageSizes]) => {
|
||||
pageSize]) => {
|
||||
freezeFieldData({
|
||||
'fileName': fileName,
|
||||
'fileSize': fileSize,
|
||||
@ -112,9 +145,9 @@ class PDFDocumentProperties {
|
||||
'producer': info.Producer,
|
||||
'version': info.PDFFormatVersion,
|
||||
'pageCount': this.pdfDocument.numPages,
|
||||
'pageSizeInch': pageSizes.inch,
|
||||
'pageSizeMM': pageSizes.mm,
|
||||
'pageSize': pageSize,
|
||||
'_currentPageNumber': currentPageNumber,
|
||||
'_pagesRotation': pagesRotation,
|
||||
});
|
||||
this._updateUI();
|
||||
|
||||
@ -191,6 +224,7 @@ class PDFDocumentProperties {
|
||||
delete this.fieldData;
|
||||
this._dataAvailableCapability = createPromiseCapability();
|
||||
this._currentPageNumber = 1;
|
||||
this._pagesRotation = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,27 +274,87 @@ class PDFDocumentProperties {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_parsePageSize(pageSizeInches) {
|
||||
_parsePageSize(pageSizeInches, pagesRotation) {
|
||||
if (!pageSizeInches) {
|
||||
return Promise.resolve({ inch: undefined, mm: undefined, });
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
// Take the viewer rotation into account as well; compare with Adobe Reader.
|
||||
if (pagesRotation % 180 !== 0) {
|
||||
pageSizeInches = {
|
||||
width: pageSizeInches.height,
|
||||
height: pageSizeInches.width,
|
||||
};
|
||||
}
|
||||
const isPortrait = isPortraitOrientation(pageSizeInches);
|
||||
|
||||
let sizeInches = {
|
||||
width: Math.round(pageSizeInches.width * 100) / 100,
|
||||
height: Math.round(pageSizeInches.height * 100) / 100,
|
||||
};
|
||||
// 1in == 25.4mm; no need to round to 2 decimals for millimeters.
|
||||
let sizeMillimeters = {
|
||||
width: Math.round(pageSizeInches.width * 25.4 * 10) / 10,
|
||||
height: Math.round(pageSizeInches.height * 25.4 * 10) / 10,
|
||||
};
|
||||
|
||||
let pageName = null;
|
||||
let name = getPageName(sizeInches, isPortrait, US_PAGE_NAMES) ||
|
||||
getPageName(sizeMillimeters, isPortrait, METRIC_PAGE_NAMES);
|
||||
|
||||
if (!name && !(Number.isInteger(sizeMillimeters.width) &&
|
||||
Number.isInteger(sizeMillimeters.height))) {
|
||||
// Attempt to improve the page name detection by falling back to fuzzy
|
||||
// matching of the metric dimensions, to account for e.g. rounding errors
|
||||
// and/or PDF files that define the page sizes in an imprecise manner.
|
||||
const exactMillimeters = {
|
||||
width: pageSizeInches.width * 25.4,
|
||||
height: pageSizeInches.height * 25.4,
|
||||
};
|
||||
const intMillimeters = {
|
||||
width: Math.round(sizeMillimeters.width),
|
||||
height: Math.round(sizeMillimeters.height),
|
||||
};
|
||||
|
||||
// Try to avoid false positives, by only considering "small" differences.
|
||||
if (Math.abs(exactMillimeters.width - intMillimeters.width) < 0.1 &&
|
||||
Math.abs(exactMillimeters.height - intMillimeters.height) < 0.1) {
|
||||
|
||||
name = getPageName(intMillimeters, isPortrait, METRIC_PAGE_NAMES);
|
||||
if (name) {
|
||||
// Update *both* sizes, computed above, to ensure that the displayed
|
||||
// dimensions always correspond to the detected page name.
|
||||
sizeInches = {
|
||||
width: Math.round(intMillimeters.width / 25.4 * 100) / 100,
|
||||
height: Math.round(intMillimeters.height / 25.4 * 100) / 100,
|
||||
};
|
||||
sizeMillimeters = intMillimeters;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name) {
|
||||
pageName = this.l10n.get('document_properties_page_size_name_' +
|
||||
name.toLowerCase(), null, name);
|
||||
}
|
||||
const { width, height, } = pageSizeInches;
|
||||
|
||||
return Promise.all([
|
||||
this.l10n.get('document_properties_page_size_in_2', {
|
||||
width: (Math.round(width * 100) / 100).toLocaleString(),
|
||||
height: (Math.round(height * 100) / 100).toLocaleString(),
|
||||
}, '{{width}} × {{height}} in'),
|
||||
// 1in = 25.4mm; no need to round to 2 decimals for millimeters.
|
||||
this.l10n.get('document_properties_page_size_mm_2', {
|
||||
width: (Math.round(width * 25.4 * 10) / 10).toLocaleString(),
|
||||
height: (Math.round(height * 25.4 * 10) / 10).toLocaleString(),
|
||||
}, '{{width}} × {{height}} mm'),
|
||||
]).then((sizes) => {
|
||||
return {
|
||||
inch: sizes[0],
|
||||
mm: sizes[1],
|
||||
};
|
||||
(this._isNonMetricLocale ? sizeInches : sizeMillimeters),
|
||||
this.l10n.get('document_properties_page_size_unit_' +
|
||||
(this._isNonMetricLocale ? 'inches' : 'millimeters'), null,
|
||||
this._isNonMetricLocale ? 'in' : 'mm'),
|
||||
pageName,
|
||||
this.l10n.get('document_properties_page_size_orientation_' +
|
||||
(isPortrait ? 'portrait' : 'landscape'), null,
|
||||
isPortrait ? 'portrait' : 'landscape'),
|
||||
]).then(([{ width, height, }, unit, name, orientation]) => {
|
||||
return this.l10n.get('document_properties_page_size_dimension_' +
|
||||
(name ? 'name_' : '') + 'string', {
|
||||
width: width.toLocaleString(),
|
||||
height: height.toLocaleString(),
|
||||
unit,
|
||||
name,
|
||||
orientation,
|
||||
}, '{{width}} × {{height}} {{unit}} (' +
|
||||
(name ? '{{name}}, ' : '') + '{{orientation}})');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,10 @@ function formatL10nValue(text, args) {
|
||||
* @implements {IL10n}
|
||||
*/
|
||||
let NullL10n = {
|
||||
getLanguage() {
|
||||
return Promise.resolve('en-us');
|
||||
},
|
||||
|
||||
getDirection() {
|
||||
return Promise.resolve('ltr');
|
||||
},
|
||||
@ -439,6 +443,10 @@ function isValidRotation(angle) {
|
||||
return Number.isInteger(angle) && angle % 90 === 0;
|
||||
}
|
||||
|
||||
function isPortraitOrientation(size) {
|
||||
return size.width <= size.height;
|
||||
}
|
||||
|
||||
function cloneObj(obj) {
|
||||
let result = Object.create(null);
|
||||
for (let i in obj) {
|
||||
@ -642,6 +650,7 @@ export {
|
||||
SCROLLBAR_PADDING,
|
||||
VERTICAL_PADDING,
|
||||
isValidRotation,
|
||||
isPortraitOrientation,
|
||||
isFileSchema,
|
||||
cloneObj,
|
||||
PresentationModeState,
|
||||
|
@ -352,11 +352,7 @@ See https://github.com/adobe-type-tools/cmap-resources
|
||||
<span data-l10n-id="document_properties_page_count">Page Count:</span> <p id="pageCountField">-</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span data-l10n-id="document_properties_page_size">Page Size:</span>
|
||||
<p>
|
||||
<span id="pageSizeFieldMM">-</span><br>
|
||||
<span id="pageSizeFieldInch">-</span>
|
||||
</p>
|
||||
<span data-l10n-id="document_properties_page_size">Page Size:</span> <p id="pageSizeField">-</p>
|
||||
</div>
|
||||
<div class="buttonRow">
|
||||
<button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
|
||||
|
@ -159,8 +159,7 @@ function getViewerConfiguration() {
|
||||
'producer': document.getElementById('producerField'),
|
||||
'version': document.getElementById('versionField'),
|
||||
'pageCount': document.getElementById('pageCountField'),
|
||||
'pageSizeInch': document.getElementById('pageSizeFieldInch'),
|
||||
'pageSizeMM': document.getElementById('pageSizeFieldMM'),
|
||||
'pageSize': document.getElementById('pageSizeField'),
|
||||
},
|
||||
},
|
||||
errorWrapper: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user