Skip to content

Commit

Permalink
feat(font): extend font declaration (#66)
Browse files Browse the repository at this point in the history
* create font face declarations and extract font faces from text document
  • Loading branch information
connium authored Feb 25, 2019
1 parent e4ab095 commit d50bbae
Show file tree
Hide file tree
Showing 22 changed files with 624 additions and 122 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.


## [Unreleased] (2019-??-??)
### Added
- **font:** extend font declaration

## [0.7.0] (2019-02-19)
### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ style1.setPageBreakBefore();
style1.setKeepTogether();
p1.setStyle(style1);
// font usage
document.declareFont('Open Sans', 'Open Sans', simpleOdf.FontPitch.Variable);
document.getFontFaceDeclarations().create('Open Sans', 'Open Sans', simpleOdf.FontPitch.Variable);
const p2 = body.addParagraph('It always seems impossible until it\'s done.');
const style2 = new simpleOdf.ParagraphStyle();
style1.setFontName('Open Sans');
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"scripts": {
"build": "tsc -p .",
"preversion": "npm test",
"postversion": "git push && git push --tags",
"prepublishOnly": "tsc",
"prepublishOnly": "npm run build",
"pretest": "npm run build",
"posttest": "npm run lint",
"test": "jest",
"watch-test": "jest --watch",
"coverage": "jest --coverage",
"lint": "tslint --project tslint.json src/**/*.ts",
"lint": "tslint -c tslint.json --project .",
"docs": "rm -r ./lib && tsc && jsdoc2md --name-format --param-list-format list --separators --partial ./jsdoc2md/body.hbs ./jsdoc2md/params-list.hbs ./jsdoc2md/returns.hbs ./jsdoc2md/scope.hbs --files ./lib/api/**/*.js ./lib/style/**/*.js > ./docs/API.md"
},
"dependencies": {
Expand Down
7 changes: 7 additions & 0 deletions src/api/meta/Meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export class Meta {
* @since 0.6.0
*/
public setCreator (creator: string | undefined): Meta {
/* tslint:disable-next-line:strict-type-predicates */
if (creator === undefined || typeof creator === 'string') {
this.creator = creator;
}
Expand Down Expand Up @@ -162,6 +163,7 @@ export class Meta {
* @since 0.6.0
*/
public setDescription (description: string | undefined): Meta {
/* tslint:disable-next-line:strict-type-predicates */
if (description === undefined || typeof description === 'string') {
this.description = description;
}
Expand Down Expand Up @@ -233,6 +235,7 @@ export class Meta {
* @since 0.6.0
*/
public setInitialCreator (initialCreator: string | undefined): Meta {
/* tslint:disable-next-line:strict-type-predicates */
if (initialCreator === undefined || typeof initialCreator === 'string') {
this.initialCreator = initialCreator;
}
Expand Down Expand Up @@ -271,6 +274,7 @@ export class Meta {
* @since 0.6.0
*/
public addKeyword (keyword: string): Meta {
/* tslint:disable-next-line:strict-type-predicates */
if (typeof keyword === 'string') {
this.keywords.push(...keyword.split(','));
}
Expand Down Expand Up @@ -420,6 +424,7 @@ export class Meta {
* @since 0.6.0
*/
public setPrintedBy (printedBy: string | undefined): Meta {
/* tslint:disable-next-line:strict-type-predicates */
if (printedBy === undefined || typeof printedBy === 'string') {
this.printedBy = printedBy;
}
Expand Down Expand Up @@ -457,6 +462,7 @@ export class Meta {
* @since 0.6.0
*/
public setSubject (subject: string | undefined): Meta {
/* tslint:disable-next-line:strict-type-predicates */
if (subject === undefined || typeof subject === 'string') {
this.subject = subject;
}
Expand Down Expand Up @@ -493,6 +499,7 @@ export class Meta {
* @since 0.6.0
*/
public setTitle (title: string | undefined): Meta {
/* tslint:disable-next-line:strict-type-predicates */
if (title === undefined || typeof title === 'string') {
this.title = title;
}
Expand Down
64 changes: 64 additions & 0 deletions src/api/office/FontFaceDeclarations.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { FontFace, FontPitch } from '../style';
import { FontFaceDeclarations } from './FontFaceDeclarations';

describe(FontFaceDeclarations.name, () => {
const testFontFamily = 'someFontFamily';
const testFontName = 'someFontName';
const testFontPitch = FontPitch.Variable;

let fontFaceDeclarations: FontFaceDeclarations;

beforeEach(() => {
fontFaceDeclarations = new FontFaceDeclarations();
});

describe('font face', () => {
it('return an empty list by default', () => {
const fonts = fontFaceDeclarations.getAll();

expect(fonts).toEqual([]);
});

it('create and return new font', () => {
const font = fontFaceDeclarations.create(testFontName, testFontFamily, testFontPitch);

expect(font).toBeInstanceOf(FontFace);
expect(font.getFontFamily()).toBe(testFontFamily);
expect(font.getFontPitch()).toBe(testFontPitch);
expect(font.getName()).toBe(testFontName);
});

it('create a font only once', () => {
const font1 = fontFaceDeclarations.create(testFontName);
const font2 = fontFaceDeclarations.create(testFontName);
const fonts = fontFaceDeclarations.getAll();

expect(font1).toBe(font2);
expect(fonts.length).toBe(1);
expect(fonts[0]).toBe(font1);
});

it('get previously created font', () => {
const font1 = fontFaceDeclarations.create(testFontName);
const font2 = fontFaceDeclarations.get(testFontName);

expect(font1).toBe(font2);
});

it('return undefined if unknown font is requested', () => {
fontFaceDeclarations.create(testFontName);

const font = fontFaceDeclarations.get('unknownFontName');

expect(font).toBeUndefined();
});

it('delete font', () => {
fontFaceDeclarations.create(testFontName);
expect(fontFaceDeclarations.getAll().length).toBe(1);

fontFaceDeclarations.delete(testFontName);
expect(fontFaceDeclarations.getAll().length).toBe(0);
});
});
});
99 changes: 99 additions & 0 deletions src/api/office/FontFaceDeclarations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { FontFace, FontPitch } from '../style';

/**
* This class contains all font face declarations of a document.
*
* It is used to manage the fonts that are used in the document.
*
* @example
* document.getFontFaceDeclarations()
* .create('FreeSans', 'FreeSans', FontPitch.Variable);
*
* @since 0.8.0
*/
export class FontFaceDeclarations {
private readonly fontFaces: Map<string, FontFace> = new Map();

/**
* Creates a {@link FontFace} object with the given name.
* If a font with this name already exists, the existing font will be returned.
*
* @example
* const fontFaceDeclarations = new FontFaceDeclarations();
* fontFaceDeclarations.create('FreeSans', 'FreeSans', FontPitch.Variable);
*
* @param {string} name The unique name for the font
* @param {string} [fontFamily] The name of the font family
* @param {FontPitch} [fontPitch] Indicator whether the font has a fixed or variable width
* @returns {FontFace} A new `FontFace` object with the specified properties
* or an existing font face, if one with the specified name exists
* @since 0.8.0
*/
public create (name: string, fontFamily?: string, fontPitch?: FontPitch): FontFace {
let fontFace = this.fontFaces.get(name);

if (fontFace !== undefined) {
return fontFace;
}

fontFace = new FontFace(name, fontFamily, fontPitch);
this.fontFaces.set(name, fontFace);

return fontFace;
}

/**
* The `get()` method returns a specified element from a Map object.
*
* @example
* const fontFaceDeclarations = new FontFaceDeclarations();
* fontFaceDeclarations.create('FreeSans');
* fontFaceDeclarations.get('UnknownFont'); // undefined
* fontFaceDeclarations.get('FreeSans'); // FreeSans font
*
* @param {string} name The name of the requested font
* @returns {FontFace | undefined} The `FontFace` object associated with the specified name
* or `undefined` if there is no font with this name
* @since 0.8.0
*/
public get (name: string): FontFace | undefined {
return this.fontFaces.get(name);
}

/**
* The `getAll()` method returns a new `Array` object that contains the fonts of the document.
*
* @example
* const fontFaceDeclarations = new FontFaceDeclarations();
* fontFaceDeclarations.create('FreeSans');
* fontFaceDeclarations.create('Symbol');
* fontFaceDeclarations.getAll(); // [FreeSans, Symbol]
*
* @returns {FontFace[]} A new `Array` object that contains the fonts of the document
* @since 0.8.0
*/
public getAll (): FontFace[] {
return [...this.fontFaces.values()];
}

/**
* The `delete()` method removes the specified font from the font face declarations.
*
* @example
* var myMap = new Map();
* const fontFaceDeclarations = new FontFaceDeclarations();
* fontFaceDeclarations.create('FreeSans');
* fontFaceDeclarations.create('Symbol');
* fontFaceDeclarations.delete('FreeSans');
* fontFaceDeclarations.get('FreeSans'); // undefined
*
* @param {string} name The name of the font to remove from the font face declarations
* @returns {Meta} The `Meta` object
* @since 0.8.0
*/
public delete (name: string): FontFaceDeclarations {
this.fontFaces.delete(name);

return this;
}
}
23 changes: 4 additions & 19 deletions src/api/office/TextDocument.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { readFile, unlink } from 'fs';
import { promisify } from 'util';
import { Meta } from '../meta/Meta';
import { FontFace } from '../style';
import { FontPitch } from '../style/FontPitch';
import { FontFaceDeclarations } from './FontFaceDeclarations';
import { TextBody } from './TextBody';
import { TextDocument, XML_DECLARATION } from './TextDocument';

Expand All @@ -26,24 +25,10 @@ describe(TextDocument.name, () => {
});

describe('font', () => {
it('return an empty list of fonts by default', () => {
const fonts = document.getFonts();
it('return a font face declarations object', () => {
const fontFaceDeclarations = document.getFontFaceDeclarations();

expect(fonts).toEqual([]);
});

it('return a font face object', () => {
const font = document.declareFont('Springfield', 'Springfield', FontPitch.Variable);

expect(font).toBeInstanceOf(FontFace);
});

it('add font face to list of fonts', () => {
document.declareFont('Springfield', 'Springfield', FontPitch.Variable);

const fonts = document.getFonts();

expect(fonts).toEqual([new FontFace('Springfield', 'Springfield', FontPitch.Variable)]);
expect(fontFaceDeclarations).toBeInstanceOf(FontFaceDeclarations);
});
});

Expand Down
57 changes: 21 additions & 36 deletions src/api/office/TextDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { promisify } from 'util';
import { XMLSerializer } from 'xmldom';
import { TextDocumentWriter } from '../../xml/TextDocumentWriter';
import { Meta } from '../meta';
import { FontFace, FontPitch } from '../style';
import { FontFaceDeclarations } from './FontFaceDeclarations';
import { TextBody } from './TextBody';

export const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>\n';
Expand All @@ -14,20 +14,28 @@ export const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>\n';
* @example
* const document = new TextDocument();
* document.getMeta().setCreator('Homer Simpson');
* document.declareFont('FreeSans', 'FreeSans', FontPitch.Variable);
* document.getFontFaceDeclarations().create('FreeSans', 'FreeSans', FontPitch.Variable);
* document.getBody().addHeading('My first document');
* document.saveFlat('/home/homer/document.fodt');
*
* @since 0.1.0
*/
export class TextDocument {
private meta: Meta;
private fonts: FontFace[];
private fontFaceDeclarations: FontFaceDeclarations;
private body: TextBody;

/**
* Creates a `TextDocument` instance that represents a OpenDocument text document.
*
* @example
* const document = new TextDocument();
*
* @since 0.1.0
*/
public constructor () {
this.meta = new Meta();
this.fonts = [];
this.fontFaceDeclarations = new FontFaceDeclarations();
this.body = new TextBody();
}

Expand All @@ -47,52 +55,29 @@ export class TextDocument {
}

/**
* The `declareFont` method creates a font face to be used in the document.
*
* **Note: There is no check whether the font exists.
* In order to be displayed properly, the font must be present on the target system.**
* The `getFonts()` method returns the font face declarations of the document.
*
* @example
* new TextDocument()
* .declareFont('FreeSans', 'FreeSans', FontPitch.Variable);
* .getFontFaceDeclarations()
* .create('FreeSans', 'FreeSans', FontPitch.Variable);
*
* @param {string} name The name of the font; this name must be set to a {@link ParagraphStyle}
* @param {string} fontFamily The name of the font family
* @param {FontPitch} fontPitch The pitch of the font
* @returns {FontFace} The declared `FontFace` object
* @since 0.4.0
* @returns {FontFaceDeclarations} An object holding the font faces of the document
* @since 0.8.0
*/
public declareFont (name: string, fontFamily: string, fontPitch: FontPitch): FontFace {
const fontFace = new FontFace(name, fontFamily, fontPitch);
this.fonts.push(fontFace);

return fontFace;
}

/**
* The `getFonts()` method returns all font face declarations for the document.
*
* @example
* const document = new TextDocument();
* document.declareFont('FreeSans', 'FreeSans', FontPitch.Variable);
* document.getFonts();
*
* @returns {FontFace[]} A copy of the list of font face declarations for the document
* @since 0.7.0
*/
public getFonts (): FontFace[] {
return Array.from(this.fonts);
public getFontFaceDeclarations (): FontFaceDeclarations {
return this.fontFaceDeclarations;
}

/**
* The `getMeta()` method returns the metadata of the document.
*
* @example
* new TextDocument.getMeta()
* new TextDocument()
* .getMeta()
* .setCreator('Homer Simpson');
*
* @returns {Meta} An object holding the metadata of the document
* @see {@link Meta}
* @since 0.6.0
*/
public getMeta (): Meta {
Expand Down
Loading

0 comments on commit d50bbae

Please sign in to comment.