feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
@@ -0,0 +1,11 @@
MIT License
Copyright (c) lillallol
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,336 @@
# outline-pdf-data-structure
## Table of contents
<!--#region toc-->
- [Table of contents](#table-of-contents)
- [Installation](#installation)
- [Description](#description)
- [Code coverage](#code-coverage)
- [Example](#example)
- [Documentation](#documentation)
- [Concretions](#concretions)
- [outlinePdfDataStructure](#----outlinepdfdatastructure)
- [Motivation](#motivation)
- [Contributing](#contributing)
- [Changelog](#changelog)
- [1.0.3](#103)
- [1.0.2](#102)
- [1.0.1](#101)
- [1.0.0](#100)
- [License](#license)
<!--#endregion toc-->
## Installation
```bash
npm install @lillallol/outline-pdf-data-structure
```
## Description
Creates a pdf outline data structure as defined in the pdf specification, from a human readable string representation of the pdf outline. You can then use that data structure to "hydrate" a real pdf outline data structure like it is done in [@lillallol/outline-pdf](https://github.com/lillallol/outline-pdf).
## Code coverage
The testing code coverage is around 90%.
## Example
<!--#region example !./src/example/example.test.ts-->
```ts
import { outlinePdfDataStructure } from "../";
describe(outlinePdfDataStructure.name, () => {
it("returns low level information about the provided outline string representation", () => {
expect(
outlinePdfDataStructure(
`
1||Document
2|-|Section 1
3|-|Section 2
4|--|Subsection 1
5|-|Section 3
6||Summary
`,
6
)
).toEqual({
outlineRootCount: 6,
outlineItems: [
{
Title: "Document",
Parent: -1,
Next: 5,
First: 1,
Last: 4,
Count: 4,
Dest: 1 - 1,
},
{
Title: "Section 1",
Parent: 0,
Next: 2,
Dest: 2 - 1,
},
{
Title: "Section 2",
Parent: 0,
Prev: 1,
Next: 4,
First: 3,
Last: 3,
Count: 1,
Dest: 3 - 1,
},
{
Title: "Subsection 1",
Parent: 2,
Dest: 4 - 1,
},
{
Title: "Section 3",
Parent: 0,
Prev: 2,
Dest: 5 - 1,
},
{
Title: "Summary",
Parent: -1,
Prev: 0,
Dest: 6 - 1,
},
],
});
});
});
```
<!--#endregion example-->
## Documentation
<!--#region documentation ./documentation.md-->
<h3 id="-concretions">Concretions</h3>
<h4 id="-concretion-outlinePdfDataStructure">
outlinePdfDataStructure
</h4>
```ts
/**
* @description
* It returns all the information needed to create a real pdf data structure.
*/
export declare function outlinePdfDataStructure(
inputOutline: string,
totalNumberOfPages: number
): outlinePdfDataStructureReturnType;
```
<details open="">
<summary id="-concretion-outlinePdfDataStructure-references">
<a href="#-concretion-outlinePdfDataStructure-references">#</a>
references
</summary>
<br>
<blockquote>
<details>
<summary id="-concretion-outlinePdfDataStructure-references-outlinePdfDataStructureReturnType">
<a href="#-concretion-outlinePdfDataStructure-references-outlinePdfDataStructureReturnType">#</a>
<b>outlinePdfDataStructureReturnType</b>
</summary>
```ts
export declare type outlinePdfDataStructureReturnType = {
/**
* @description
* It returns a low level programmatic representation of the outline.
*/
outlineItems: outlineItem[];
/**
* @description
* The total number of outline nodes.
*/
outlineRootCount: number;
};
```
</details>
<blockquote>
<details>
<summary id="-concretion-outlinePdfDataStructure-references-outlinePdfDataStructureReturnType-outlineItem">
<a href="#-concretion-outlinePdfDataStructure-references-outlinePdfDataStructureReturnType-outlineItem">#</a>
<b>outlineItem</b>
</summary>
```ts
export declare type outlineItem = {
/**
* @description
* The title that will be visible in the outline of the pdf for the context
* outline node.
*/
Title: string;
/**
* @description
* The index (of the array that contains all the outline nodes) of the
* parent outline node of the context outline node.
*
* Outline nodes of depth `0` have `-1` for this value.
*/
Parent: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the
* previous sibling of the context outline node. It is `undefined` for the
* case there is no previous sibling.
*/
Prev?: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the next
* sibling of the context outline node. It is `undefined` for the case there
* is no next sibling.
*/
Next?: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the first
* immediate child of the context outline node. It is `undefined` for the
* case there is no immediate child.
*/
First?: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the last
* immediate child of the context outline node. It is `undefined` for the
* case there is no immediate child.
*/
Last?: number;
/**
* @description
* Total number of outline nodes that are descendants to the context outline
* node.
*/
Count?: number;
/**
* @description
* The page of the pdf that the outline node hyper links to.
*/
Dest: number;
};
```
</details>
</blockquote>
</blockquote>
</details>
<hr>
<!--#endregion documentation -->
## Motivation
This whole package was part of [@lillallol/outline-pdf](https://github.com/lillallol/outline-pdf), but then I decide to do separation of concerns, because it would make both projects more readable.
## Contributing
I am open to suggestions/pull request to improve this program.
You will find the following commands useful:
- Clones the github repository of this project:
```bash
git clone https://github.com/lillallol/outline-pdf-data-structure
```
- Installs the node modules (nothing will work without them):
```bash
npm install
```
- Tests the code and generates code coverage:
```bash
npm run test
```
The generated code coverage is saved in `./coverage`.
- Lints the source folder using typescript and eslint:
```bash
npm run lint
```
- Builds the typescript code from the `./src` folder to javascript code in `./dist`:
```bash
npm run build-ts
```
- Injects in place the generated toc and imported files to `README.md`:
```bash
npm run build-md
```
- Checks the project for spelling mistakes:
```bash
npm run spell-check
```
Take a look at the related configuration `./cspell.json`.
- Checks `./src` for dead typescript files:
```bash
npm run dead-files
```
Take a look at the related configuration `./unimportedrc.json`.
## Changelog
### 1.0.3
- Added documentation section in `README.md`. The documentation was generated via [ts-doc-gen-md](https://www.npmjs.com/package/ts-doc-gen-md) and added in the `README.md` via [md-in-place](https://www.npmjs.com/package/md-in-place).
- Added changelog section in `README.md`.
- Added contributing section in `README.md`.
- Added example section in `README.md`.
- Did some internal changes in the code (added `tagUnindent`, created files: `errorMessages.ts`,`constants.ts`,`publicApi.ts`).
### 1.0.2
**bug fixes**
- Changed count to be number of descendants instead of immediate descendants.
### 1.0.1
Some minor link fixes in the `README.md`.
### 1.0.0
First release.
## License
MIT
@@ -0,0 +1,3 @@
export declare const constants: {
trimmedTocLineValidPattern: RegExp;
};
@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.constants = void 0;
exports.constants = {
trimmedTocLineValidPattern: /^(?<collapse>[+\-]?)(?<pageNumber>\d+)\|(?<depth>-*)\|(?<title>.*)$/
};
@@ -0,0 +1,6 @@
import { IOutline } from "../publicApi";
/**
* @description Returns the index of the last immediate child for the provided outline node.
* It throws if there is no child.
*/
export declare function getIndexOfLastImmediateChild(outline: IOutline, i: number): number;
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIndexOfLastImmediateChild = void 0;
const errorMessages_1 = require("../errorMessages");
/**
* @description Returns the index of the last immediate child for the provided outline node.
* It throws if there is no child.
*/
function getIndexOfLastImmediateChild(outline, i) {
let candidateIndex;
const parentDepth = outline[i].depth;
for (let ii = i + 1; ii < outline.length; ii++) {
if (outline[ii].depth <= parentDepth)
break;
if (outline[ii].depth === parentDepth + 1)
candidateIndex = ii;
}
if (candidateIndex === undefined)
throw Error(errorMessages_1.internalErrorMessages.internalLibraryError);
return candidateIndex;
}
exports.getIndexOfLastImmediateChild = getIndexOfLastImmediateChild;
@@ -0,0 +1,6 @@
import { IOutline } from "../publicApi";
/**
* @description It returns the index of the immediate next sibling for the specified outline node.
* It throws if it does not exist.
*/
export declare function getIndexOfImmediateNextSibling(outline: IOutline, i: number): number;
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIndexOfImmediateNextSibling = void 0;
const errorMessages_1 = require("../errorMessages");
/**
* @description It returns the index of the immediate next sibling for the specified outline node.
* It throws if it does not exist.
*/
function getIndexOfImmediateNextSibling(outline, i) {
const contextDepth = outline[i].depth;
for (let ii = i + 1; ii < outline.length; ii++) {
if (outline[ii].depth < contextDepth)
break;
if (outline[ii].depth === contextDepth)
return ii;
}
throw Error(errorMessages_1.internalErrorMessages.internalLibraryError);
}
exports.getIndexOfImmediateNextSibling = getIndexOfImmediateNextSibling;
@@ -0,0 +1,6 @@
import type { IOutline } from "../publicApi";
/**
* @description It returns the index of the parent of the provided outline node.
* It returns `-1` when the provided outline node has zero depth.
*/
export declare function getIndexOfImmediateParent(outline: IOutline, i: number): number;
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIndexOfImmediateParent = void 0;
/**
* @description It returns the index of the parent of the provided outline node.
* It returns `-1` when the provided outline node has zero depth.
*/
function getIndexOfImmediateParent(outline, i) {
const contextDepth = outline[i].depth;
for (let ii = i; ii > -1; ii--) {
if (contextDepth - 1 === outline[ii].depth)
return ii;
}
return -1;
}
exports.getIndexOfImmediateParent = getIndexOfImmediateParent;
@@ -0,0 +1,6 @@
import type { IOutline } from "../publicApi";
/**
* @description It returns the index of the immediate previous sibling for the provided outline node,
* or throws if it does not find one.
*/
export declare function getIndexOfImmediatePreviousSibling(outline: IOutline, i: number): number;
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIndexOfImmediatePreviousSibling = void 0;
const errorMessages_1 = require("../errorMessages");
/**
* @description It returns the index of the immediate previous sibling for the provided outline node,
* or throws if it does not find one.
*/
function getIndexOfImmediatePreviousSibling(outline, i) {
const contextDepth = outline[i].depth;
for (let ii = i - 1; ii > -1; ii--) {
if (outline[ii].depth < contextDepth)
break;
if (outline[ii].depth === contextDepth)
return ii;
}
throw Error(errorMessages_1.internalErrorMessages.internalLibraryError);
}
exports.getIndexOfImmediatePreviousSibling = getIndexOfImmediatePreviousSibling;
@@ -0,0 +1,5 @@
import type { IOutline } from "../publicApi";
/**
* @description It returns the number of descendant outline nodes of the provided outline node.
*/
export declare function getNumberOfDescendants(outline: IOutline, i: number): number;
@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getNumberOfDescendants = void 0;
/**
* @description It returns the number of descendant outline nodes of the provided outline node.
*/
function getNumberOfDescendants(outline, i) {
let count = 0;
const contextDepth = outline[i].depth;
for (let ii = i + 1; ii < outline.length; ii++) {
if (contextDepth < outline[ii].depth) {
count++;
}
else {
break;
}
}
return count;
}
exports.getNumberOfDescendants = getNumberOfDescendants;
@@ -0,0 +1,5 @@
import type { IOutline } from "../publicApi";
/**
* @description Returns a predicate on whether the provided outline node has a child.
*/
export declare function hasChild(outline: IOutline, i: number): boolean;
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasChild = void 0;
/**
* @description Returns a predicate on whether the provided outline node has a child.
*/
function hasChild(outline, i) {
if (i === outline.length - 1)
return false;
return outline[i].depth + 1 === outline[i + 1].depth;
}
exports.hasChild = hasChild;
@@ -0,0 +1,5 @@
import type { IOutline } from "../publicApi";
/**
* @description It returns a predicate on whether the provided outline node has an immediate next sibling.
*/
export declare function hasImmediateNextSibling(outline: IOutline, i: number): boolean;
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasImmediateNextSibling = void 0;
const getIndexOfImmediateNextSibling_1 = require("./getIndexOfImmediateNextSibling");
/**
* @description It returns a predicate on whether the provided outline node has an immediate next sibling.
*/
function hasImmediateNextSibling(outline, i) {
try {
getIndexOfImmediateNextSibling_1.getIndexOfImmediateNextSibling(outline, i);
return true;
}
catch (e) {
return false;
}
}
exports.hasImmediateNextSibling = hasImmediateNextSibling;
@@ -0,0 +1,5 @@
import type { IOutline } from "../publicApi";
/**
* @description It returns a predicate on whether the provided outline node has an immediate previous sibling.
*/
export declare function hasImmediatePreviousSibling(outline: IOutline, i: number): boolean;
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasImmediatePreviousSibling = void 0;
const getIndexOfImmediatePreviousSibling_1 = require("./getIndexOfImmediatePreviousSibling");
/**
* @description It returns a predicate on whether the provided outline node has an immediate previous sibling.
*/
function hasImmediatePreviousSibling(outline, i) {
try {
getIndexOfImmediatePreviousSibling_1.getIndexOfImmediatePreviousSibling(outline, i);
return true;
}
catch (e) {
return false;
}
}
exports.hasImmediatePreviousSibling = hasImmediatePreviousSibling;
@@ -0,0 +1,6 @@
import type { outlinePdfDataStructureReturnType } from "../publicApi";
/**
* @description
* It returns all the information needed to create a real pdf data structure.
*/
export declare function outlinePdfDataStructure(inputOutline: string, totalNumberOfPages: number): outlinePdfDataStructureReturnType;
@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.outlinePdfDataStructure = void 0;
const hasChild_1 = require("./hasChild");
const getIndexOfImmediateLastChild_1 = require("./getIndexOfImmediateLastChild");
const getIndexOfImmediateNextSibling_1 = require("./getIndexOfImmediateNextSibling");
const getIndexOfImmediatePreviousSibling_1 = require("./getIndexOfImmediatePreviousSibling");
const hasImmediatePreviousSibling_1 = require("./hasImmediatePreviousSibling");
const hasImmediateNextSibling_1 = require("./hasImmediateNextSibling");
const getIndexOfImmediateParentFactory_1 = require("./getIndexOfImmediateParentFactory");
const printedToOutline_1 = require("./printedToOutline");
const getNumberOfDescendants_1 = require("./getNumberOfDescendants");
/**
* @description
* It returns all the information needed to create a real pdf data structure.
*/
function outlinePdfDataStructure(inputOutline, totalNumberOfPages) {
const outlineItems = [];
const outline = printedToOutline_1.printedToOutline(inputOutline, totalNumberOfPages);
outline.forEach((node, i) => {
node.count = getNumberOfDescendants_1.getNumberOfDescendants(outline, i) * (node.collapse ? -1 : 1);
});
for (let i = 0; i < outline.length; i++) {
outlineItems[i] = {
Title: outline[i].title,
Parent: getIndexOfImmediateParentFactory_1.getIndexOfImmediateParent(outline, i),
...(hasImmediatePreviousSibling_1.hasImmediatePreviousSibling(outline, i) && {
Prev: getIndexOfImmediatePreviousSibling_1.getIndexOfImmediatePreviousSibling(outline, i),
}),
...(hasImmediateNextSibling_1.hasImmediateNextSibling(outline, i) && {
Next: getIndexOfImmediateNextSibling_1.getIndexOfImmediateNextSibling(outline, i),
}),
...(hasChild_1.hasChild(outline, i) && {
First: i + 1,
Last: getIndexOfImmediateLastChild_1.getIndexOfLastImmediateChild(outline, i),
Count: outline[i].count,
}),
Dest: outline[i].pageNumber - 1,
};
}
return {
outlineItems: outlineItems,
outlineRootCount: outline.length,
};
}
exports.outlinePdfDataStructure = outlinePdfDataStructure;
@@ -0,0 +1,26 @@
import type { IOutline } from "../publicApi";
/**
* @description Converts the outline string representation to its programmatic representation.
* It throws if the outline string representation is not valid.
* ```ts
* //input
* printedToOutline(`
* 1||Document
* 2|-|Section 1
* -3|-|Section 2
* 4|--|Subsection 1
* 5|-|Section 3
* 6||Summary
* `,6)
* //output
* [
* { pageNumber: 1, depth: 0, title: "Document" , collapse : false , line : "1||Document"},
* { pageNumber: 2, depth: 1, title: "Section 1" , collapse : false , line : "2|-|Section 1"},
* { pageNumber: 3, depth: 1, title: "Section 2" , collapse : true , line : "-3|-|Section 2"},
* { pageNumber: 4, depth: 2, title: "Subsection 1" , collapse : false , line : "4|--|Subsection 1"},
* { pageNumber: 5, depth: 1, title: "Section 3" , collapse : false , line : "5|-|Section 3"},
* { pageNumber: 6, depth: 0, title: "Summary" , collapse : false , line : "6||Summary"},
* ]
* ```
*/
export declare function printedToOutline(inputOutline: string, totalNumberOfPages: number): IOutline;
@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.printedToOutline = void 0;
const constants_1 = require("../constants");
const errorMessages_1 = require("../errorMessages");
/**
* @description Converts the outline string representation to its programmatic representation.
* It throws if the outline string representation is not valid.
* ```ts
* //input
* printedToOutline(`
* 1||Document
* 2|-|Section 1
* -3|-|Section 2
* 4|--|Subsection 1
* 5|-|Section 3
* 6||Summary
* `,6)
* //output
* [
* { pageNumber: 1, depth: 0, title: "Document" , collapse : false , line : "1||Document"},
* { pageNumber: 2, depth: 1, title: "Section 1" , collapse : false , line : "2|-|Section 1"},
* { pageNumber: 3, depth: 1, title: "Section 2" , collapse : true , line : "-3|-|Section 2"},
* { pageNumber: 4, depth: 2, title: "Subsection 1" , collapse : false , line : "4|--|Subsection 1"},
* { pageNumber: 5, depth: 1, title: "Section 3" , collapse : false , line : "5|-|Section 3"},
* { pageNumber: 6, depth: 0, title: "Summary" , collapse : false , line : "6||Summary"},
* ]
* ```
*/
function printedToOutline(inputOutline, totalNumberOfPages) {
if (inputOutline.trim() === "")
throw Error(errorMessages_1.errorMessages.emptyOutline);
let lastNode;
const toReturn = inputOutline
.trim()
.split("\n")
.map((untrimmedLine, i) => {
const line = untrimmedLine.trim();
const match = line.match(constants_1.constants.trimmedTocLineValidPattern);
if (match === null)
throw Error(errorMessages_1.errorMessages.wrongPatternInLine(line));
const { groups } = match;
if (groups === undefined)
throw Error(errorMessages_1.internalErrorMessages.internalLibraryError);
const { pageNumber, depth, title, collapse } = groups;
if (pageNumber === undefined)
throw Error(errorMessages_1.internalErrorMessages.internalLibraryError);
if (title === undefined)
throw Error(errorMessages_1.internalErrorMessages.internalLibraryError);
const nodeToReturn = {
pageNumber: Number(pageNumber),
depth: depth === undefined ? 0 : depth.length,
title: title,
collapse: (() => {
if (collapse === undefined)
return false;
if (collapse === "-")
return true;
if (collapse === "+")
return false;
if (collapse === "")
return false;
throw Error(errorMessages_1.internalErrorMessages.internalLibraryError);
})(),
line: line,
};
if (nodeToReturn.pageNumber === 0)
throw Error(errorMessages_1.errorMessages.zeroPageInOutlineIsNotAllowed(line));
if (nodeToReturn.pageNumber > totalNumberOfPages)
throw Error(errorMessages_1.errorMessages.pageNumberInOutlineExceedsMaximum(line, totalNumberOfPages));
if (i === 0 && nodeToReturn.depth !== 0)
throw Error(errorMessages_1.errorMessages.depthOfOutlineHasToStartWithZero);
if (i !== 0) {
if (!(nodeToReturn.depth <= lastNode.depth + 1)) {
throw Error(errorMessages_1.errorMessages.wrongDepthDisplacement(lastNode.line, nodeToReturn.line));
}
if (nodeToReturn.pageNumber < lastNode.pageNumber) {
throw Error(errorMessages_1.errorMessages.invalidDisplacementOfPage(lastNode.line, nodeToReturn.line));
}
if (lastNode.collapse && lastNode.depth >= nodeToReturn.depth) {
throw Error(errorMessages_1.errorMessages.nodeIsCollapsedWithoutChildren(lastNode.line));
}
}
lastNode = nodeToReturn;
return nodeToReturn;
});
return toReturn;
}
exports.printedToOutline = printedToOutline;
@@ -0,0 +1,13 @@
export declare const errorMessages: {
nodeIsCollapsedWithoutChildren: (line: string) => string;
emptyOutline: string;
wrongDepthDisplacement: (oldLine: string, newLine: string) => string;
zeroPageInOutlineIsNotAllowed: (line: string) => string;
pageNumberInOutlineExceedsMaximum: (line: string, max: number) => string;
depthOfOutlineHasToStartWithZero: string;
wrongPatternInLine: (line: string) => string;
invalidDisplacementOfPage: (oldLine: string, newLine: string) => string;
};
export declare const internalErrorMessages: {
internalLibraryError: string;
};
@@ -0,0 +1,68 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.internalErrorMessages = exports.errorMessages = void 0;
const constants_1 = require("./constants");
const es_utils_1 = require("./es-utils");
exports.errorMessages = {
nodeIsCollapsedWithoutChildren: (line) => es_utils_1.tagUnindent `
Outline node:
${line}
has no children and it is collapsed. You have to un collapse it or add children.
`,
emptyOutline: "no outline has been provided",
wrongDepthDisplacement: (oldLine, newLine) => es_utils_1.tagUnindent `
Wrong depth displacement for the following part of the outline:
${oldLine}
${newLine}
`,
zeroPageInOutlineIsNotAllowed: (line) => es_utils_1.tagUnindent `
Zero page number is not allowed in outline:
${line}
`,
pageNumberInOutlineExceedsMaximum: (line, max) => es_utils_1.tagUnindent `
Pdf file has:
${max}
number of pages and outline points out of this range:
${line}
`,
depthOfOutlineHasToStartWithZero: `The outline should start with zero depth.`,
wrongPatternInLine: (line) => es_utils_1.tagUnindent `
The line:
${line}
has wrong pattern. It does satisfy the following regular expression:
${constants_1.constants.trimmedTocLineValidPattern.source}
`,
invalidDisplacementOfPage: (oldLine, newLine) => es_utils_1.tagUnindent `
The page is not displaced correctly:
${oldLine}
${newLine}
`
};
exports.internalErrorMessages = {
internalLibraryError: es_utils_1.tagUnindent `
Something went wrong. If you have not used the library in a way
it is not supposed to be used, then copy this error message and
open an issue here:
https://github.com/lillallol/outline-pdf-data-structure/issues
`,
};
@@ -0,0 +1,19 @@
/**
* @description
* It returns the common maximum indentation length among the lines of the
* provided string. Lines that are only spaces are not taken into account.
*
* It throws error if a `\s` character that is not ` ` (space) is encountered
* in the indentation.
* @example
* commonMinIndentLength(
* ` hello
* world!
* `);
* //returns
* 3;
*/
export declare function commonMaxIndentLength(s: string): number;
export declare const _errorMessages: {
badIndentSpaceCharacter: string;
};
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports._errorMessages = exports.commonMaxIndentLength = void 0;
/**
* @description
* It returns the common maximum indentation length among the lines of the
* provided string. Lines that are only spaces are not taken into account.
*
* It throws error if a `\s` character that is not ` ` (space) is encountered
* in the indentation.
* @example
* commonMinIndentLength(
* ` hello
* world!
* `);
* //returns
* 3;
*/
function commonMaxIndentLength(s) {
let minIndentLength = Infinity;
s.split("\n").forEach((line) => {
if (/^[ ]+$/.test(line) || line.length === 0)
return;
let newMinIndentLength = 0;
for (let i = 0; i < line.length; i++) {
if (/\s/.test(line[i]) && line[i] !== " ")
throw Error(exports._errorMessages.badIndentSpaceCharacter);
if (line[i] !== " ")
break;
newMinIndentLength++;
}
if (newMinIndentLength < minIndentLength)
minIndentLength = newMinIndentLength;
});
return minIndentLength;
}
exports.commonMaxIndentLength = commonMaxIndentLength;
exports._errorMessages = {
badIndentSpaceCharacter: "Only space characters are allowed in the indented part of the string",
};
@@ -0,0 +1,5 @@
/**
* @description
* Return the last line of the provided string.
*/
export declare function getLastLineOfString(string: string): string;
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLastLineOfString = void 0;
/**
* @description
* Return the last line of the provided string.
*/
function getLastLineOfString(string) {
const lines = string.split("\n");
return lines[lines.length - 1];
}
exports.getLastLineOfString = getLastLineOfString;
@@ -0,0 +1 @@
export { tagUnindent } from "./tagUnindent";
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tagUnindent = void 0;
var tagUnindent_1 = require("./tagUnindent");
Object.defineProperty(exports, "tagUnindent", { enumerable: true, get: function () { return tagUnindent_1.tagUnindent; } });
@@ -0,0 +1,30 @@
/**
* @description
* Tag function that returns the template literal it is provided as a string, but
* with its common maximum indentation removed.
*
* The placeholders that are provided as single string element arrays are multi-line indented.
*
* It throws if :
*
* - the first line is non empty
* - the last line is not only spaces
* - the string contains `\s` characters that are not space characters in its indentation
*
* @example
* expect(
* tagUnindent`
* path : (${`"./some/where"`})
* index : ${0}
* message :
* ${["hello\nworld"]}
* `
* ).toBe(
* `path : ("./some/where")\n` +
* `index : 0\n` +
* `message : \n` +
* ` hello\n` +
* ` world`
* );
*/
export declare function tagUnindent(stringArray: TemplateStringsArray, ...placeholders: (number | string | [string])[]): string;
@@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tagUnindent = void 0;
const getLastLineOfString_1 = require("./getLastLineOfString");
const unindent_1 = require("./unindent");
/**
* @description
* Tag function that returns the template literal it is provided as a string, but
* with its common maximum indentation removed.
*
* The placeholders that are provided as single string element arrays are multi-line indented.
*
* It throws if :
*
* - the first line is non empty
* - the last line is not only spaces
* - the string contains `\s` characters that are not space characters in its indentation
*
* @example
* expect(
* tagUnindent`
* path : (${`"./some/where"`})
* index : ${0}
* message :
* ${["hello\nworld"]}
* `
* ).toBe(
* `path : ("./some/where")\n` +
* `index : 0\n` +
* `message : \n` +
* ` hello\n` +
* ` world`
* );
*/
function tagUnindent(stringArray, ...placeholders) {
const stringToUnindent = (() => {
if (placeholders.length === 0)
return stringArray[0];
let toReturn = "";
for (let i = 0; i < placeholders.length; i++) {
const currentPlaceholder = placeholders[i];
if (Array.isArray(currentPlaceholder)) {
const lastStringArrayLineLength = getLastLineOfString_1.getLastLineOfString(stringArray[i]).length;
const [placeholderSingleElementArrayString] = currentPlaceholder;
toReturn =
toReturn +
stringArray[i] +
placeholderSingleElementArrayString
.split("\n")
.map((line, i) => {
if (i === 0)
return line;
return " ".repeat(lastStringArrayLineLength) + line;
})
.join("\n");
}
else {
toReturn = toReturn + stringArray[i] + currentPlaceholder;
}
}
return toReturn + stringArray[stringArray.length - 1];
})();
return unindent_1.unindent(stringToUnindent);
}
exports.tagUnindent = tagUnindent;
@@ -0,0 +1,42 @@
/**
* @description
* It removes the first and last line of the provided string. From the remaining lines it
* calculates the common maximum indentation, ignoring empty lines, and then it subtracts it from
* each non empty line.
*
* The subtraction happens also for empty lines that have length bigger than the common minimum
* indentation length. If they have length less than the common minimum indentation length they are
* replaced with an empty string;
*
* - If `\s` characters are encountered in the indentation string that are not ` ` (space), it throws error.
* - If the first line is not an empty string, it throws error.
* - If the last line is not only space characters it throws error.
* @example
* unindent(
* `
* transaction tree string representation
*
* |_ "p"
* |_ "a"
* |_ "t"
* |_ "h"
* | |_ "prop1" -1 => 1
* |_ "t"
* |_ "prop" "11" => "1"
* `);
* //returns
* ` transaction tree string representation
*
* |_ "p"
* |_ "a"
* |_ "t"
* |_ "h"
* | |_ "prop1" -1 => 1
* |_ "t"
* |_ "prop" "11" => "1"`;
*/
export declare function unindent(s: string): string;
export declare const _errorMessage: {
badFirstLine: string;
badLastLine: string;
};
@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports._errorMessage = exports.unindent = void 0;
const commonMaxIndentLength_1 = require("./commonMaxIndentLength");
/**
* @description
* It removes the first and last line of the provided string. From the remaining lines it
* calculates the common maximum indentation, ignoring empty lines, and then it subtracts it from
* each non empty line.
*
* The subtraction happens also for empty lines that have length bigger than the common minimum
* indentation length. If they have length less than the common minimum indentation length they are
* replaced with an empty string;
*
* - If `\s` characters are encountered in the indentation string that are not ` ` (space), it throws error.
* - If the first line is not an empty string, it throws error.
* - If the last line is not only space characters it throws error.
* @example
* unindent(
* `
* transaction tree string representation
*
* |_ "p"
* |_ "a"
* |_ "t"
* |_ "h"
* | |_ "prop1" -1 => 1
* |_ "t"
* |_ "prop" "11" => "1"
* `);
* //returns
* ` transaction tree string representation
*
* |_ "p"
* |_ "a"
* |_ "t"
* |_ "h"
* | |_ "prop1" -1 => 1
* |_ "t"
* |_ "prop" "11" => "1"`;
*/
function unindent(s) {
const lines = s.split("\n");
if (lines[0] !== "")
throw Error(exports._errorMessage.badFirstLine);
if (!/^[ ]*$/.test(lines[lines.length - 1]))
throw Error(exports._errorMessage.badLastLine);
const minIndentLength = commonMaxIndentLength_1.commonMaxIndentLength(s);
return s
.split("\n")
.slice(1, -1)
.map((line) => {
if (/^\s*$/.test(line) && line.length < minIndentLength)
return "";
return line.slice(minIndentLength, line.length);
})
.join("\n");
}
exports.unindent = unindent;
exports._errorMessage = {
badFirstLine: `first line has to be an empty string`,
badLastLine: "last line has to be only space characters",
};
@@ -0,0 +1 @@
export { outlinePdfDataStructure } from "./core/outlinePdfDataStructure";
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.outlinePdfDataStructure = void 0;
var outlinePdfDataStructure_1 = require("./core/outlinePdfDataStructure");
Object.defineProperty(exports, "outlinePdfDataStructure", { enumerable: true, get: function () { return outlinePdfDataStructure_1.outlinePdfDataStructure; } });
@@ -0,0 +1,76 @@
export declare type IOutline = IOutlineNode[];
export declare type IOutlineNode = {
depth: number;
pageNumber: number;
title: string;
collapse: boolean;
count?: number;
line: string;
};
export declare type outlinePdfDataStructureReturnType = {
/**
* @description
* It returns a low level programmatic representation of the outline.
*/
outlineItems: outlineItem[];
/**
* @description
* The total number of outline nodes.
*/
outlineRootCount: number;
};
export declare type outlineItem = {
/**
* @description
* The title that will be visible in the outline of the pdf for the context
* outline node.
*/
Title: string;
/**
* @description
* The index (of the array that contains all the outline nodes) of the
* parent outline node of the context outline node.
*
* Outline nodes of depth `0` have `-1` for this value.
*/
Parent: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the
* previous sibling of the context outline node. It is `undefined` for the
* case there is no previous sibling.
*/
Prev?: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the next
* sibling of the context outline node. It is `undefined` for the case there
* is no next sibling.
*/
Next?: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the first
* immediate child of the context outline node. It is `undefined` for the
* case there is no immediate child.
*/
First?: number;
/**
* @description
* The index (of the array that contains all the outline nodes) of the last
* immediate child of the context outline node. It is `undefined` for the
* case there is no immediate child.
*/
Last?: number;
/**
* @description
* Total number of outline nodes that are descendants to the context outline
* node.
*/
Count?: number;
/**
* @description
* The page of the pdf that the outline node hyper links to.
*/
Dest: number;
};
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,60 @@
{
"name": "@lillallol/outline-pdf-data-structure",
"version": "1.0.3",
"description": "Create a pdf outline data structure from a high level string representation of pdf outline. This data structure can be used to create a real pdf outline.",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"scripts": {
"check-updates": "ncu",
"update": "npx ncu -u;npm install",
"dead-files": "unimported",
"spell-check": "cspell \"./**/*\"",
"lint": "tsc --noEmit && eslint ./src/**/*.ts --max-warnings=0",
"test": "rm -rf ./coverage && npx jest --clearCache && npx jest ./src --coverage --silent",
"build-ts": "rm -rf ./dist;npx tsc -p ./tsconfig.build.json",
"build-md": "rm -rf ./documentation.md; node ./doc-gen.js; npx md-in-place",
"build": "npm run build-ts;npm run build-md;",
"pre-bump": "npm run dead-files && npm run spell-check && npm run lint && npm run test && npm run build && git add --all && git commit",
"release": "npm run pre-bump && npm publish --access public && npm run after-bump",
"bump-patch": "npm run pre-bump && npm version patch && npm publish && npm run after-bump",
"bump-minor": "npm run pre-bump && npm version minor && npm publish && npm run after-bump",
"bump-major": "npm run pre-bump && npm version major && npm publish && npm run after-bump",
"after-bump": "git push origin master && rm -rf ./dist"
},
"repository": {
"type": "git",
"url": "git+https://github.com/lillallol/outline-pdf-data-structure.git"
},
"keywords": [
"outline",
"pdf",
"index",
"data-structure",
"bookmarks",
"pdf specification"
],
"author": "lillallol",
"license": "MIT",
"files": [
"dist"
],
"bugs": {
"url": "https://github.com/lillallol/outline-pdf-data-structure/issues"
},
"homepage": "https://github.com/lillallol/outline-pdf-data-structure#readme",
"devDependencies": {
"@types/jest": "^26.0.23",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"cspell": "^5.4.1",
"eslint": "^7.26.0",
"jest": "^26.6.3",
"md-in-place": "^1.0.1",
"npm-check-updates": "^11.5.13",
"prettier": "^2.3.0",
"ts-doc-gen-md": "*",
"ts-jest": "^26.5.6",
"typescript": "^4.2.4",
"unimported": "^1.11.0"
}
}