Skip to content

Commit

Permalink
feat(paragraph): declare font on the text document and set font name …
Browse files Browse the repository at this point in the history
…to paragraph style (#33)

Fixes #27
  • Loading branch information
connium authored May 18, 2018
1 parent 0ef4bc6 commit 90bb536
Show file tree
Hide file tree
Showing 15 changed files with 200 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- **chore:** Add code coverage analysis with Codecov by @oncletom, closes [#17](https://github.com/connium/simple-odf/issues/17)
- **chore:** Add static code analysis with Better Code Hub
- **paragraph:** Set color, font size and typeface to the text of a paragraph, closes [#26](https://github.com/connium/simple-odf/issues/26)
- **paragraph:** Set font family to a paragraph, closes [#27](https://github.com/connium/simple-odf/issues/27)

### Changed
- **chore:** Run TSLint on production and test code
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ style1.setTypeface(simpleOdf.Typeface.Bold);
style1.setHorizontalAlignment(simpleOdf.HorizontalAlignment.Center);
style1.setPageBreakBefore();
p1.setStyle(style1);
// font usage
document.declareFont("Open Sans", "Open Sans", simpleOdf.FontPitch.Variable);
const p2 = document.addParagraph("It always seems impossible until it's done.");
const style2 = new simpleOdf.ParagraphStyle();
style1.setFontName("Open Sans");

document.addHeading("Credits", 2);

Expand Down
5 changes: 3 additions & 2 deletions src/OdfAttributeName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ export enum OdfAttributeName {
FormatFontStyle = "fo:font-style",
FormatFontWeight = "fo:font-weight",
FormatTextAlign = "fo:text-align",

OfficeMimetype = "office:mimetype",
OfficeVersion = "office:version",

StyleFamily = "style:family",
StyleFontName = "style:font-name",
StyleName = "style:name",
StylePosition = "style:position",
StyleType = "style:type",
Expand Down
6 changes: 4 additions & 2 deletions src/OdfElementName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ export enum OdfElementName {
OfficeBinaryData = "office:binary-data",
OfficeBody = "office:body",
OfficeDocument = "office:document",
OfficeFontFaceDeclarations = "office:font-face-decls",
OfficeText = "office:text",

StyleFontFace = "style:font-face",
StyleParagraphProperties = "style:paragraph-properties",
StyleStyle = "style:style",
StyleTabStop = "style:tab-stop",
StyleTabStops = "style:tab-stops",
StyleTextProperties = "style:text-properties",
StyleParagraphProperties = "style:paragraph-properties",

TextHyperlink = "text:a",
TextHeading = "text:h",
TextHyperlink = "text:a",
TextLineBreak = "text:line-break",
TextList = "text:list",
TextListItem = "text:list-item",
Expand Down
55 changes: 55 additions & 0 deletions src/TextDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DOMImplementation, XMLSerializer } from "xmldom";
import { OdfAttributeName } from "./OdfAttributeName";
import { OdfElement } from "./OdfElement";
import { OdfElementName } from "./OdfElementName";
import { FontPitch } from "./style/FontPitch";
import { Heading } from "./text/Heading";
import { List } from "./text/List";
import { Paragraph } from "./text/Paragraph";
Expand All @@ -13,13 +14,39 @@ export const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>\n';

const OFFICE_VERSION = "1.2";

/** This interface holds a font font declaration */
interface IFont {
name: string;
fontFamily: string;
fontPitch: FontPitch;
}

/**
* This class represents an empty ODF text document.
* @since 0.1.0
*/
export class TextDocument extends OdfElement {
private fonts: IFont[];

public constructor() {
super();

this.fonts = [];
}

/**
* Declares a font 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.**
*
* @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 ptich of the fonr
* @since 0.4.0
*/
public declareFont(name: string, fontFamily: string, fontPitch: FontPitch): void {
this.fonts.push({ name, fontFamily, fontPitch });
}

/**
Expand Down Expand Up @@ -104,6 +131,8 @@ export class TextDocument extends OdfElement {
root.setAttribute(OdfAttributeName.OfficeMimetype, "application/vnd.oasis.opendocument.text");
root.setAttribute(OdfAttributeName.OfficeVersion, OFFICE_VERSION);

this.setFontFaceElements(document, root);

const bodyElement = document.createElement(OdfElementName.OfficeBody);
root.appendChild(bodyElement);

Expand All @@ -112,4 +141,30 @@ export class TextDocument extends OdfElement {

super.toXml(document, textElement);
}

/**
* Adds the `font-face-decls` element and the font faces if any font needs to bne declared.
*
* @param {Document} document The XML document
* @param {Element} root The element which will be used as parent
*/
private setFontFaceElements(document: Document, root: Element): void {
if (this.fonts.length === 0) {
return;
}

root.setAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");

const fontFaceDeclsElement = document.createElement(OdfElementName.OfficeFontFaceDeclarations);
root.appendChild(fontFaceDeclsElement);

this.fonts.forEach((font: IFont) => {
const fontFaceElement = document.createElement(OdfElementName.StyleFontFace);
fontFaceDeclsElement.appendChild(fontFaceElement);
fontFaceElement.setAttribute("style:name", font.name);
const encodedFontFamily = font.fontFamily.includes(" ") === true ? `'${font.fontFamily}'` : font.fontFamily;
fontFaceElement.setAttribute("svg:font-family", encodedFontFamily);
fontFaceElement.setAttribute("style:font-pitch", font.fontPitch);
});
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { Image } from "./draw/Image";

// style
export { Color } from "./style/Color";
export { FontPitch } from "./style/FontPitch";
export { HorizontalAlignment } from "./style/HorizontalAlignment";
export { IParagraphStyle } from "./style/IParagraphStyle";
export { ParagraphStyle } from "./style/ParagraphStyle";
Expand Down
4 changes: 4 additions & 0 deletions src/style/FontPitch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum FontPitch {
Fixed = "fixed",
Variable = "variable",
}
17 changes: 17 additions & 0 deletions src/style/ITextProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ export interface ITextProperties {
*/
getColor(): Color | undefined;

/**
* Sets the name of the font that will be applied to the text.
* To reset the font, `undefined` must be given.
*
* @param {string} name The name of the font to apply or `undefined` if the default font should be used
* @since 0.4.0
*/
setFontName(name: string): void;

/**
* Returns the name of the font that will be applied to the text or `undefined` if the default font will be used.
*
* @returns {string | undefined} The name of the font to apply or `undefined` if the default font will be used
* @since 0.4.0
*/
getFontName(): string | undefined;

/**
* Sets the font size that will be applied to the text.
*
Expand Down
19 changes: 11 additions & 8 deletions src/style/ParagraphProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,20 @@ export class ParagraphProperties implements IParagraphProperties {
/**
* Adds the `tab-stops` element and the tab stop definitions if any tab stop is set.
*
* @param {Element} textPropertiesElement The element which will take the attribute
* @param {Document} document The XML document
* @param {Element} textPropertiesElement The element which will be used as parent
*/
private setTabStopElements(document: Document, paragraphPropertiesElement: Element): void {
if (this.tabStops.length > 0) {
const tabStopsElement = document.createElement(OdfElementName.StyleTabStops);
paragraphPropertiesElement.appendChild(tabStopsElement);

this.tabStops.forEach((tabStop: TabStop) => {
tabStop.toXml(document, tabStopsElement);
});
if (this.tabStops.length === 0) {
return;
}

const tabStopsElement = document.createElement(OdfElementName.StyleTabStops);
paragraphPropertiesElement.appendChild(tabStopsElement);

this.tabStops.forEach((tabStop: TabStop) => {
tabStop.toXml(document, tabStopsElement);
});
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/style/ParagraphStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ export class ParagraphStyle implements IParagraphStyle {
return this.textProperties.getColor();
}

/** @inheritDoc */
public setFontName(name: string): void {
this.textProperties.setFontName(name);
}

/** @inheritDoc */
public getFontName(): string | undefined {
return this.textProperties.getFontName();
}

/** @inheritDoc */
public setFontSize(size: number): void {
return this.textProperties.setFontSize(size);
Expand Down Expand Up @@ -129,6 +139,7 @@ export class ParagraphStyle implements IParagraphStyle {
// text properties
const color = this.textProperties.getColor();
hash.update(color !== undefined ? color.toHex() : "");
hash.update(this.textProperties.getFontName() || "");
hash.update(this.textProperties.getFontSize().toString());
hash.update(this.textProperties.getTypeface().toString());

Expand Down
4 changes: 3 additions & 1 deletion src/style/StyleHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ export class StyleHelper {
rootNode.setAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0");
rootNode.setAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");

const officeBodyElement = rootNode.getElementsByTagName(OdfElementName.OfficeBody)[0];

const automaticStyles = document.createElement(OdfElementName.OfficeAutomaticStyles);
rootNode.insertBefore(automaticStyles, rootNode.firstChild);
rootNode.insertBefore(automaticStyles, officeBodyElement);

return automaticStyles;
}
Expand Down
26 changes: 26 additions & 0 deletions src/style/TextProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const DEFAULT_TYPEFACE = Typeface.Normal;
*/
export class TextProperties implements ITextProperties {
private color: Color | undefined;
private fontName: string | undefined;
private fontSize: number;
private typeface: Typeface;

Expand All @@ -39,6 +40,16 @@ export class TextProperties implements ITextProperties {
return this.color;
}

/** @inheritDoc */
public setFontName(name: string): void {
this.fontName = name;
}

/** @inheritDoc */
public getFontName(): string | undefined {
return this.fontName;
}

/** @inheritDoc */
public setFontSize(size: number): void {
this.fontSize = Math.max(size, MINIMAL_FONT_SIZE);
Expand Down Expand Up @@ -67,6 +78,7 @@ export class TextProperties implements ITextProperties {
*/
public isDefault(): boolean {
return this.color === undefined
&& this.fontName === undefined
&& this.fontSize === DEFAULT_FONT_SIZE
&& this.typeface === DEFAULT_TYPEFACE;
}
Expand All @@ -87,6 +99,7 @@ export class TextProperties implements ITextProperties {
parent.appendChild(textPropertiesElement);

this.setColorAttribute(textPropertiesElement);
this.setFontNameAttribute(textPropertiesElement);
this.setFontSizeAttribute(textPropertiesElement);
this.setFontStyleAttribute(textPropertiesElement);
this.setFontWeightAttribute(textPropertiesElement);
Expand All @@ -105,6 +118,19 @@ export class TextProperties implements ITextProperties {
textPropertiesElement.setAttribute(OdfAttributeName.FormatColor, this.color.toHex());
}

/**
* Sets the `font-name` attribute if a font name is set.
*
* @param {Element} textPropertiesElement The element which will take the attribute
*/
private setFontNameAttribute(textPropertiesElement: Element): void {
if (this.fontName === undefined) {
return;
}

textPropertiesElement.setAttribute(OdfAttributeName.StyleFontName, this.fontName);
}

/**
* Sets the `font-size` attribute if the font size is different from the default font size.
*
Expand Down
23 changes: 23 additions & 0 deletions test/TextDocument.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readFile, unlink } from "fs";
import { promisify } from "util";
import { FontPitch } from "../src/style/FontPitch";
import { HorizontalAlignment } from "../src/style/HorizontalAlignment";
import { Heading } from "../src/text/Heading";
import { List } from "../src/text/List";
Expand All @@ -25,6 +26,28 @@ describe(TextDocument.name, () => {
done();
});

describe("#declareFont", () => {
it("add svg namespace", () => {
document.declareFont("Springfield", "Springfield", FontPitch.Variable);

expect(document.toString()).toMatch(/xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"/);
});

it("add font declaration to document", () => {
document.declareFont("Springfield", "Springfield", FontPitch.Variable);

/* tslint:disable-next-line:max-line-length */
expect(document.toString()).toMatch(/<office:font-face-decls><style:font-face style:name="Springfield" svg:font-family="Springfield" style:font-pitch="variable"\/><\/office:font-face-decls>/);
});

it("add font declaration to document and wrap font family if it contains spaces", () => {
document.declareFont("Homer Simpson", "Homer Simpson", FontPitch.Variable);

/* tslint:disable-next-line:max-line-length */
expect(document.toString()).toMatch(/<office:font-face-decls><style:font-face style:name="Homer Simpson" svg:font-family="'Homer Simpson'" style:font-pitch="variable"\/><\/office:font-face-decls>/);
});
});

describe("#addHeading", () => {
it("return a heading", () => {
const heading = document.addHeading();
Expand Down
9 changes: 9 additions & 0 deletions test/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { unlink } from "fs";
import { join } from "path";
import { promisify } from "util";
import { Color } from "../src/style/Color";
import { FontPitch } from "../src/style/FontPitch";
import { HorizontalAlignment } from "../src/style/HorizontalAlignment";
import { ParagraphStyle } from "../src/style/ParagraphStyle";
import { TabStop } from "../src/style/TabStop";
Expand Down Expand Up @@ -72,6 +73,14 @@ xdescribe("integration", () => {
paragraph.getStyle().setColor(Color.fromRgb(62, 180, 137));
});

it("font name", () => {
document.declareFont("Open Sans", "Open Sans", FontPitch.Variable);

const paragraph = document.addParagraph("Open Sans");
paragraph.setStyle(new ParagraphStyle());
paragraph.getStyle().setFontName("Open Sans");
});

it("font size", () => {
const paragraph = document.addParagraph("Some small text");
paragraph.setStyle(new ParagraphStyle());
Expand Down
Loading

0 comments on commit 90bb536

Please sign in to comment.