Compare commits
No commits in common. 'main' and 'v0.30.4' have entirely different histories.
@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: "[BUG]"
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Write...
|
|
||||||
2. Render...
|
|
||||||
3. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
|
||||||
- OS: [e.g. Arch Linux]
|
|
||||||
- Architecture: [e.g. x86_64, ARM]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: "[FEATURE]"
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
@ -1,42 +0,0 @@
|
|||||||
name: Build and Test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main, develop ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main, develop ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Cache build data
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
target
|
|
||||||
~/.cargo/
|
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-
|
|
||||||
- name: Build
|
|
||||||
run: cargo build --verbose --all-features
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --verbose --all-features
|
|
||||||
|
|
||||||
- name: Test init
|
|
||||||
run: cargo run -- init
|
|
||||||
|
|
||||||
- name: Test HTML
|
|
||||||
run: cargo run -- render README.md README.html --format html
|
|
||||||
|
|
||||||
- name: Test PDF
|
|
||||||
run: cargo run --all-features -- render README.md README.pdf --format pdf
|
|
File diff suppressed because it is too large
Load Diff
@ -1,167 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: $body-background;
|
|
||||||
overflow-x: hidden;
|
|
||||||
color: $primary-color;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
font-family: "Fira Sans", "Noto Sans", SansSerif, sans-serif;
|
|
||||||
width: 100vh;
|
|
||||||
max-width: calc(100% - 4rem);
|
|
||||||
padding: 2rem;
|
|
||||||
margin: auto;
|
|
||||||
background-color: $background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 0.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100vh;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
color: $primary-color;
|
|
||||||
|
|
||||||
pre {
|
|
||||||
font-family: "Fira Code", "Mono", monospace;
|
|
||||||
padding: 0.8em 0.2em;
|
|
||||||
background-color: $code-background !important;
|
|
||||||
border-radius: 0.25em;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.inlineCode {
|
|
||||||
font-family: "Fira Code", monospace;
|
|
||||||
border-radius: 0.1em;
|
|
||||||
background-color: $code-background;
|
|
||||||
padding: 0 0.1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tableWrapper {
|
|
||||||
overflow-x: auto;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
& > table {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
|
|
||||||
tr {
|
|
||||||
&:nth-child(odd) {
|
|
||||||
background-color: $table-background-alt;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(1) {
|
|
||||||
background-color: $table-background-alt;
|
|
||||||
font-weight: bold;
|
|
||||||
border-bottom: 1px solid invert($background-color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table td, table th {
|
|
||||||
border-left: 1px solid invert($background-color);
|
|
||||||
padding: 0.2em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr td:first-child, table tr th:first-child {
|
|
||||||
border-left: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin-left: 0;
|
|
||||||
padding-top: 0.2em;
|
|
||||||
padding-bottom: 0.2em;
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $secondary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote {
|
|
||||||
border-left: 0.3em solid $quote-background-alt;
|
|
||||||
border-radius: 0.2em;
|
|
||||||
padding-left: 1em;
|
|
||||||
margin-left: 0;
|
|
||||||
background-color: $quote-background;
|
|
||||||
|
|
||||||
.metadata {
|
|
||||||
font-style: italic;
|
|
||||||
padding-left: 0.5em;
|
|
||||||
color: $primary-variant-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.figure {
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.imageDescription {
|
|
||||||
display: block;
|
|
||||||
color: $primary-variant-1;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.centered {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glossaryReference {
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
border-bottom: 1px dotted $primary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow {
|
|
||||||
font-family: "Fira Code", "Mono", monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
|
|
||||||
.content > section > section, .content > section > section {
|
|
||||||
page-break-inside: avoid;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: $background-color !important;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$background-color: #1e1d2c;
|
|
||||||
$background-color-variant-1: lighten($background-color, 7%);
|
|
||||||
$background-color-variant-2: lighten($background-color, 14%);
|
|
||||||
$background-color-variant-3: lighten($background-color, 21%);
|
|
||||||
|
|
||||||
$primary-color: #EEE;
|
|
||||||
$primary-variant-1: darken($primary-color, 14%);
|
|
||||||
$secondary-color: #3aa7df;
|
|
||||||
|
|
||||||
$body-background: darken($background-color, 5%);
|
|
||||||
$code-background: lighten($background-color, 5%);
|
|
||||||
$table-background-alt: $background-color-variant-2;
|
|
||||||
$quote-background: lighten($background-color-variant-1, 3%);
|
|
||||||
$quote-background-alt: $background-color-variant-3;
|
|
@ -1,20 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$background-color: darken(#2b303b, 8%);
|
|
||||||
$background-color-variant-1: lighten($background-color, 7%);
|
|
||||||
$background-color-variant-2: lighten($background-color, 14%);
|
|
||||||
$background-color-variant-3: lighten($background-color, 21%);
|
|
||||||
|
|
||||||
$primary-color: #EEE;
|
|
||||||
$primary-variant-1: darken($primary-color, 14%);
|
|
||||||
$secondary-color: #3aa7df;
|
|
||||||
|
|
||||||
$body-background: darken($background-color, 5%);
|
|
||||||
$code-background: $background-color-variant-1;
|
|
||||||
$table-background-alt: $background-color-variant-2;
|
|
||||||
$quote-background: lighten($background-color-variant-1, 3%);
|
|
||||||
$quote-background-alt: $background-color-variant-3;
|
|
@ -1,19 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$background-color: darken(#002b36, 5%);
|
|
||||||
$background-color-variant-1: lighten($background-color, 7%);
|
|
||||||
$background-color-variant-2: lighten($background-color, 14%);
|
|
||||||
$background-color-variant-3: lighten($background-color, 21%);
|
|
||||||
$primary-color: #EEE;
|
|
||||||
$primary-variant-1: darken($primary-color, 14%);
|
|
||||||
$secondary-color: #0096c9;
|
|
||||||
|
|
||||||
$body-background: darken($background-color, 3%);
|
|
||||||
$code-background: $background-color-variant-1;
|
|
||||||
$table-background-alt: lighten($background-color, 10%);
|
|
||||||
$quote-background: lighten($background-color-variant-1, 3%);
|
|
||||||
$quote-background-alt: $background-color-variant-3;
|
|
@ -1,19 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$background-color: #FFF;
|
|
||||||
$background-color-variant-1: darken($background-color, 7%);
|
|
||||||
$background-color-variant-2: darken($background-color, 14%);
|
|
||||||
$background-color-variant-3: darken($background-color, 21%);
|
|
||||||
$primary-color: #000;
|
|
||||||
$primary-variant-1: lighten($primary-color, 14%);
|
|
||||||
$secondary-color: #00286a;
|
|
||||||
|
|
||||||
$body-background: $background-color-variant-1;
|
|
||||||
$code-background: $background-color-variant-1;
|
|
||||||
$table-background-alt: $background-color-variant-2;
|
|
||||||
$quote-background: $background-color-variant-2;
|
|
||||||
$quote-background-alt: $background-color-variant-3;
|
|
@ -1,19 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$background-color: #FFF;
|
|
||||||
$background-color-variant-1: darken($background-color, 7%);
|
|
||||||
$background-color-variant-2: darken($background-color, 14%);
|
|
||||||
$background-color-variant-3: darken($background-color, 21%);
|
|
||||||
$primary-color: #112;
|
|
||||||
$primary-variant-1: lighten($primary-color, 14%);
|
|
||||||
$secondary-color: #00348e;
|
|
||||||
|
|
||||||
$body-background: $background-color-variant-1;
|
|
||||||
$code-background: $background-color-variant-1;
|
|
||||||
$table-background-alt: $background-color-variant-2;
|
|
||||||
$quote-background: $background-color-variant-2;
|
|
||||||
$quote-background-alt: $background-color-variant-3;
|
|
@ -1,19 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$background-color: #fff8f0;
|
|
||||||
$background-color-variant-1: darken($background-color, 4%);
|
|
||||||
$background-color-variant-2: darken($background-color, 8%);
|
|
||||||
$background-color-variant-3: darken($background-color, 12%);
|
|
||||||
$primary-color: #112;
|
|
||||||
$primary-variant-1: lighten($primary-color, 14%);
|
|
||||||
$secondary-color: #2b61be;
|
|
||||||
|
|
||||||
$body-background: $background-color-variant-1;
|
|
||||||
$code-background: $background-color-variant-1;
|
|
||||||
$table-background-alt: $background-color-variant-2;
|
|
||||||
$quote-background: $background-color-variant-2;
|
|
||||||
$quote-background-alt: $background-color-variant-3;
|
|
@ -1,9 +1,3 @@
|
|||||||
<!--
|
|
||||||
~ Snekdown - Custom Markdown flavour and parser
|
|
||||||
~ Copyright (C) 2021 Trivernis
|
|
||||||
~ See LICENSE for more information.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<div style="font-size: 10px; text-align: center; width: 100%;">
|
<div style="font-size: 10px; text-align: center; width: 100%;">
|
||||||
<span class="pageNumber"></span>/<span class="totalPages"></span>
|
<span class="pageNumber"></span>/<span class="totalPages"></span>
|
||||||
</div>
|
</div>
|
@ -0,0 +1,149 @@
|
|||||||
|
body {
|
||||||
|
background-color: #DDD;
|
||||||
|
overflow-x: hidden;
|
||||||
|
color: #000;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
font-family: "Fira Sans", "Noto Sans", SansSerif, sans-serif;
|
||||||
|
width: 100vh;
|
||||||
|
max-width: calc(100% - 4rem);
|
||||||
|
padding: 2rem;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100vh;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
code pre {
|
||||||
|
font-family: "Fira Code", "Mono", monospace;
|
||||||
|
padding: 0.8em 0.2em;
|
||||||
|
background-color: #EEE !important;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.inlineCode {
|
||||||
|
font-family: "Fira Code", monospace;
|
||||||
|
border-radius: 0.1em;
|
||||||
|
background-color: #EEE;
|
||||||
|
padding: 0 0.1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableWrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableWrapper > table {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(odd) {
|
||||||
|
background-color: #DDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(1) {
|
||||||
|
background-color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td, table th {
|
||||||
|
border-left: 1px solid black;
|
||||||
|
padding: 0.2em 0.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr td:first-child, table tr th:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin-left: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
border-left: 0.3em solid gray;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
padding-left: 1em;
|
||||||
|
margin-left: 0;
|
||||||
|
background-color: #EEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote .metadata {
|
||||||
|
font-style: italic;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
color: #444
|
||||||
|
}
|
||||||
|
|
||||||
|
.figure {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.figure .imageDescription {
|
||||||
|
display: block;
|
||||||
|
color: #444;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glossaryReference {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
border-bottom: 1px dotted #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
font-family: "Fira Code", "Mono", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.content > section > section, .content > section > section {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,2 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub mod html_writer;
|
pub mod html_writer;
|
||||||
pub mod to_html;
|
pub mod to_html;
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use crate::settings::style_settings::Theme;
|
|
||||||
use std::time::Instant;
|
|
||||||
use syntect::highlighting::ThemeSet;
|
|
||||||
use syntect::parsing::SyntaxSet;
|
|
||||||
|
|
||||||
/// Returns the css of a theme compiled from sass
|
|
||||||
pub fn get_css_for_theme(theme: Theme) -> String {
|
|
||||||
let start = Instant::now();
|
|
||||||
let vars = match theme {
|
|
||||||
Theme::GitHub => include_str!("assets/light-github.scss"),
|
|
||||||
Theme::SolarizedDark => include_str!("assets/dark-solarized.scss"),
|
|
||||||
Theme::SolarizedLight => include_str!("assets/light-solarized.scss"),
|
|
||||||
Theme::OceanDark => include_str!("assets/dark-ocean.scss"),
|
|
||||||
Theme::OceanLight => include_str!("assets/light-ocean.scss"),
|
|
||||||
Theme::MagicDark => include_str!("assets/dark-magic.scss"),
|
|
||||||
};
|
|
||||||
let style = format!("{}\n{}", vars, include_str!("assets/base.scss"));
|
|
||||||
|
|
||||||
let css = compile_sass(&*style);
|
|
||||||
|
|
||||||
log::debug!("Compiled style in {} ms", start.elapsed().as_millis());
|
|
||||||
|
|
||||||
css
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the syntax theme for a given theme
|
|
||||||
pub fn get_code_theme_for_theme(theme: Theme) -> (syntect::highlighting::Theme, SyntaxSet) {
|
|
||||||
lazy_static::lazy_static! { static ref PS: SyntaxSet = SyntaxSet::load_defaults_nonewlines(); }
|
|
||||||
lazy_static::lazy_static! { static ref TS: ThemeSet = ThemeSet::load_defaults(); }
|
|
||||||
|
|
||||||
let theme = match theme {
|
|
||||||
Theme::GitHub => "InspiredGitHub",
|
|
||||||
Theme::SolarizedDark => "Solarized (dark)",
|
|
||||||
Theme::SolarizedLight => "Solarized (light)",
|
|
||||||
Theme::OceanDark => "base16-ocean.dark",
|
|
||||||
Theme::OceanLight => "base16-ocean.light",
|
|
||||||
Theme::MagicDark => "base16-ocean.dark",
|
|
||||||
};
|
|
||||||
|
|
||||||
return (TS.themes[theme].clone(), PS.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile_sass(sass: &str) -> String {
|
|
||||||
String::from_utf8(
|
|
||||||
rsass::compile_scss(
|
|
||||||
sass.as_bytes(),
|
|
||||||
rsass::output::Format {
|
|
||||||
style: rsass::output::Style::Compressed,
|
|
||||||
precision: 5,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
pub const BIB_REF_DISPLAY: &str = "bib-ref-display";
|
||||||
|
pub const META_LANG: &str = "language";
|
||||||
|
|
||||||
|
// import and include options
|
||||||
|
pub const IMP_IGNORE: &str = "ignored-imports";
|
||||||
|
pub const IMP_STYLESHEETS: &str = "included-stylesheets";
|
||||||
|
pub const IMP_CONFIGS: &str = "included-configs";
|
||||||
|
pub const IMP_BIBLIOGRAPHY: &str = "included-bibliography";
|
||||||
|
pub const IMP_GLOSSARY: &str = "included-glossary";
|
||||||
|
pub const EMBED_EXTERNAL: &str = "embed-external";
|
||||||
|
pub const SMART_ARROWS: &str = "smart-arrows";
|
||||||
|
pub const INCLUDE_MATHJAX: &str = "include-math-jax";
|
||||||
|
|
||||||
|
// PDF options
|
||||||
|
pub const PDF_DISPLAY_HEADER_FOOTER: &str = "pfd-display-header-footer";
|
||||||
|
pub const PDF_HEADER_TEMPLATE: &str = "pdf-header-template";
|
||||||
|
pub const PDF_FOOTER_TEMPLATE: &str = "pdf-footer-template";
|
||||||
|
pub const PDF_MARGIN_TOP: &str = "pdf-margin-top";
|
||||||
|
pub const PDF_MARGIN_BOTTOM: &str = "pdf-margin-bottom";
|
||||||
|
pub const PDF_MARGIN_LEFT: &str = "pdf-margin-left";
|
||||||
|
pub const PDF_MARGIN_RIGHT: &str = "pdf-margin-right";
|
||||||
|
pub const PDF_PAGE_HEIGHT: &str = "pdf-page-height";
|
||||||
|
pub const PDF_PAGE_WIDTH: &str = "pdf-page-width";
|
||||||
|
pub const PDF_PAGE_SCALE: &str = "pdf-page-scale";
|
@ -0,0 +1,188 @@
|
|||||||
|
use crate::elements::MetadataValue;
|
||||||
|
use crate::references::configuration::keys::{
|
||||||
|
BIB_REF_DISPLAY, META_LANG, PDF_DISPLAY_HEADER_FOOTER, PDF_FOOTER_TEMPLATE,
|
||||||
|
PDF_HEADER_TEMPLATE, PDF_MARGIN_BOTTOM, PDF_MARGIN_TOP,
|
||||||
|
};
|
||||||
|
use crate::references::templates::Template;
|
||||||
|
use serde::export::TryFrom;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
pub(crate) mod keys;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Value {
|
||||||
|
String(String),
|
||||||
|
Bool(bool),
|
||||||
|
Float(f64),
|
||||||
|
Integer(i64),
|
||||||
|
Template(Template),
|
||||||
|
Array(Vec<Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ConfigEntry {
|
||||||
|
inner: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ConfigRefEntry = Arc<RwLock<ConfigEntry>>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Configuration {
|
||||||
|
config: Arc<RwLock<HashMap<String, ConfigRefEntry>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn as_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Value::String(string) => string.clone(),
|
||||||
|
Value::Integer(int) => format!("{}", int),
|
||||||
|
Value::Float(f) => format!("{:02}", f),
|
||||||
|
Value::Bool(b) => format!("{}", b),
|
||||||
|
Value::Array(a) => a.iter().fold("".to_string(), |a, b| {
|
||||||
|
format!("{} \"{}\"", a, b.as_string())
|
||||||
|
}),
|
||||||
|
_ => "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the bool value if the value is a boolean
|
||||||
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
|
match self {
|
||||||
|
Value::Bool(b) => Some(*b),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_float(&self) -> Option<f64> {
|
||||||
|
match self {
|
||||||
|
Value::Float(v) => Some(*v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigEntry {
|
||||||
|
pub fn new(value: Value) -> Self {
|
||||||
|
Self { inner: value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, value: Value) {
|
||||||
|
self.inner = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> &Value {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Configuration {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut self_config = Self::new();
|
||||||
|
self_config.set(BIB_REF_DISPLAY, Value::String("{{number}}".to_string()));
|
||||||
|
self_config.set(META_LANG, Value::String("en".to_string()));
|
||||||
|
self_config.set(PDF_MARGIN_BOTTOM, Value::Float(0.5));
|
||||||
|
self_config.set(PDF_MARGIN_TOP, Value::Float(0.5));
|
||||||
|
self_config.set(PDF_DISPLAY_HEADER_FOOTER, Value::Bool(true));
|
||||||
|
self_config.set(
|
||||||
|
PDF_HEADER_TEMPLATE,
|
||||||
|
Value::String("<div></div>".to_string()),
|
||||||
|
);
|
||||||
|
self_config.set(
|
||||||
|
PDF_FOOTER_TEMPLATE,
|
||||||
|
Value::String(
|
||||||
|
include_str!("../../format/chromium_pdf/assets/default-footer-template.html")
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
self_config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Configuration {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
config: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the value of a config entry
|
||||||
|
pub fn get_entry(&self, key: &str) -> Option<ConfigEntry> {
|
||||||
|
let config = self.config.read().unwrap();
|
||||||
|
if let Some(entry) = config.get(key) {
|
||||||
|
let value = entry.read().unwrap();
|
||||||
|
Some(value.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns a config entry that is a reference to a value
|
||||||
|
pub fn get_ref_entry(&self, key: &str) -> Option<ConfigRefEntry> {
|
||||||
|
let config = self.config.read().unwrap();
|
||||||
|
if let Some(entry) = config.get(&key.to_string()) {
|
||||||
|
Some(Arc::clone(entry))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a config parameter
|
||||||
|
pub fn set(&mut self, key: &str, value: Value) {
|
||||||
|
let mut config = self.config.write().unwrap();
|
||||||
|
if let Some(entry) = config.get(&key.to_string()) {
|
||||||
|
entry.write().unwrap().set(value)
|
||||||
|
} else {
|
||||||
|
config.insert(
|
||||||
|
key.to_string(),
|
||||||
|
Arc::new(RwLock::new(ConfigEntry::new(value))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a config value based on a metadata value
|
||||||
|
pub fn set_from_meta(&mut self, key: &str, value: MetadataValue) {
|
||||||
|
match value {
|
||||||
|
MetadataValue::String(string) => self.set(key, Value::String(string)),
|
||||||
|
MetadataValue::Bool(bool) => self.set(key, Value::Bool(bool)),
|
||||||
|
MetadataValue::Float(f) => self.set(key, Value::Float(f)),
|
||||||
|
MetadataValue::Integer(i) => self.set(key, Value::Integer(i)),
|
||||||
|
MetadataValue::Template(t) => self.set(key, Value::Template(t)),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_from_toml(&mut self, value: &toml::Value) -> Option<()> {
|
||||||
|
let table = value.as_table().cloned()?;
|
||||||
|
table.iter().for_each(|(k, v)| {
|
||||||
|
match v {
|
||||||
|
toml::Value::Table(_) => self.set_from_toml(v).unwrap_or(()),
|
||||||
|
_ => self.set(k, Value::try_from(v.clone()).unwrap()),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<toml::Value> for Value {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: toml::Value) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
toml::Value::Table(_) => Err(()),
|
||||||
|
toml::Value::Float(f) => Ok(Value::Float(f)),
|
||||||
|
toml::Value::Integer(i) => Ok(Value::Integer(i)),
|
||||||
|
toml::Value::String(s) => Ok(Value::String(s)),
|
||||||
|
toml::Value::Boolean(b) => Ok(Value::Bool(b)),
|
||||||
|
toml::Value::Datetime(dt) => Ok(Value::String(dt.to_string())),
|
||||||
|
toml::Value::Array(a) => Ok(Value::Array(
|
||||||
|
a.iter()
|
||||||
|
.cloned()
|
||||||
|
.filter_map(|e| Value::try_from(e).ok())
|
||||||
|
.collect::<Vec<Value>>(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,5 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub mod bibliography;
|
pub mod bibliography;
|
||||||
|
pub mod configuration;
|
||||||
pub mod glossary;
|
pub mod glossary;
|
||||||
pub mod placeholders;
|
pub mod placeholders;
|
||||||
pub mod templates;
|
pub mod templates;
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct FeatureSettings {
|
|
||||||
pub embed_external: bool,
|
|
||||||
pub smart_arrows: bool,
|
|
||||||
pub include_mathjax: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FeatureSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
embed_external: true,
|
|
||||||
smart_arrows: true,
|
|
||||||
include_mathjax: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct ImageSettings {
|
|
||||||
pub format: Option<String>,
|
|
||||||
pub max_width: Option<u32>,
|
|
||||||
pub max_height: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ImageSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
format: None,
|
|
||||||
max_height: None,
|
|
||||||
max_width: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct ImportSettings {
|
|
||||||
pub ignored_imports: Vec<String>,
|
|
||||||
pub included_stylesheets: Vec<String>,
|
|
||||||
pub included_bibliography: Vec<String>,
|
|
||||||
pub included_glossaries: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ImportSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
ignored_imports: Vec::with_capacity(0),
|
|
||||||
included_stylesheets: vec!["style.css".to_string()],
|
|
||||||
included_bibliography: vec!["Bibliography.toml".to_string()],
|
|
||||||
included_glossaries: vec!["Glossary.toml".to_string()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct MetadataSettings {
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub author: Option<String>,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub keywords: Vec<String>,
|
|
||||||
pub language: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MetadataSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
title: None,
|
|
||||||
author: None,
|
|
||||||
description: None,
|
|
||||||
keywords: Vec::new(),
|
|
||||||
language: "en".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use crate::elements::{Metadata, MetadataValue};
|
|
||||||
use crate::settings::feature_settings::FeatureSettings;
|
|
||||||
use crate::settings::image_settings::ImageSettings;
|
|
||||||
use crate::settings::import_settings::ImportSettings;
|
|
||||||
use crate::settings::metadata_settings::MetadataSettings;
|
|
||||||
use crate::settings::pdf_settings::PDFSettings;
|
|
||||||
use crate::settings::style_settings::StyleSettings;
|
|
||||||
use config::{ConfigError, Source};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{self, Display};
|
|
||||||
use std::io;
|
|
||||||
use std::mem;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub mod feature_settings;
|
|
||||||
pub mod image_settings;
|
|
||||||
pub mod import_settings;
|
|
||||||
pub mod metadata_settings;
|
|
||||||
pub mod pdf_settings;
|
|
||||||
pub mod style_settings;
|
|
||||||
|
|
||||||
pub type SettingsResult<T> = Result<T, SettingsError>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum SettingsError {
|
|
||||||
IoError(io::Error),
|
|
||||||
ConfigError(ConfigError),
|
|
||||||
TomlError(toml::ser::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for SettingsError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::IoError(e) => write!(f, "IO Error: {}", e),
|
|
||||||
Self::ConfigError(e) => write!(f, "Config Error: {}", e),
|
|
||||||
Self::TomlError(e) => write!(f, "Toml Error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for SettingsError {}
|
|
||||||
|
|
||||||
impl From<io::Error> for SettingsError {
|
|
||||||
fn from(e: io::Error) -> Self {
|
|
||||||
Self::IoError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigError> for SettingsError {
|
|
||||||
fn from(e: ConfigError) -> Self {
|
|
||||||
Self::ConfigError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<toml::ser::Error> for SettingsError {
|
|
||||||
fn from(e: toml::ser::Error) -> Self {
|
|
||||||
Self::TomlError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
|
||||||
pub struct Settings {
|
|
||||||
pub metadata: MetadataSettings,
|
|
||||||
pub features: FeatureSettings,
|
|
||||||
pub imports: ImportSettings,
|
|
||||||
pub pdf: PDFSettings,
|
|
||||||
pub images: ImageSettings,
|
|
||||||
pub style: StyleSettings,
|
|
||||||
pub custom_attributes: HashMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Source for Settings {
|
|
||||||
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
|
|
||||||
Box::new(self.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect(&self) -> Result<HashMap<String, config::Value>, config::ConfigError> {
|
|
||||||
let source_str =
|
|
||||||
toml::to_string(&self).map_err(|e| config::ConfigError::Foreign(Box::new(e)))?;
|
|
||||||
let result = toml::de::from_str(&source_str)
|
|
||||||
.map_err(|e| config::ConfigError::Foreign(Box::new(e)))?;
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Settings {
|
|
||||||
/// Loads the settings from the specified path
|
|
||||||
pub fn load(path: PathBuf) -> SettingsResult<Self> {
|
|
||||||
let mut settings = config::Config::default();
|
|
||||||
settings
|
|
||||||
.merge(Self::default())?
|
|
||||||
.merge(config::File::from(path))?;
|
|
||||||
let settings: Self = settings.try_into()?;
|
|
||||||
|
|
||||||
Ok(settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Merges the current settings with the settings from the given path
|
|
||||||
/// returning updated settings
|
|
||||||
pub fn merge(&mut self, path: PathBuf) -> SettingsResult<()> {
|
|
||||||
let mut settings = config::Config::default();
|
|
||||||
settings
|
|
||||||
.merge(self.clone())?
|
|
||||||
.merge(config::File::from(path))?;
|
|
||||||
let mut settings: Self = settings.try_into()?;
|
|
||||||
mem::swap(self, &mut settings); // replace the old settings with the new ones
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn append_metadata<M: Metadata>(&mut self, metadata: M) {
|
|
||||||
let entries = metadata.get_string_map();
|
|
||||||
for (key, value) in entries {
|
|
||||||
self.custom_attributes.insert(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_from_meta(&mut self, key: &str, value: MetadataValue) {
|
|
||||||
self.custom_attributes
|
|
||||||
.insert(key.to_string(), value.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct PDFSettings {
|
|
||||||
pub display_header_footer: bool,
|
|
||||||
pub header_template: Option<String>,
|
|
||||||
pub footer_template: Option<String>,
|
|
||||||
pub page_height: Option<f32>,
|
|
||||||
pub page_width: Option<f32>,
|
|
||||||
pub page_scale: f32,
|
|
||||||
pub margin: PDFMarginSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct PDFMarginSettings {
|
|
||||||
pub top: Option<f32>,
|
|
||||||
pub bottom: Option<f32>,
|
|
||||||
pub left: Option<f32>,
|
|
||||||
pub right: Option<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PDFMarginSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
top: Some(0.5),
|
|
||||||
bottom: Some(0.5),
|
|
||||||
left: None,
|
|
||||||
right: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PDFSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
display_header_footer: true,
|
|
||||||
header_template: Some("<div></div>".to_string()),
|
|
||||||
footer_template: Some(
|
|
||||||
include_str!("../format/chromium_pdf/assets/default-footer-template.html")
|
|
||||||
.to_string(),
|
|
||||||
),
|
|
||||||
page_height: None,
|
|
||||||
page_width: None,
|
|
||||||
page_scale: 1.0,
|
|
||||||
margin: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct StyleSettings {
|
|
||||||
pub bib_ref_display: String,
|
|
||||||
pub theme: Theme,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for StyleSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
bib_ref_display: "{{number}}".to_string(),
|
|
||||||
theme: Theme::GitHub,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub enum Theme {
|
|
||||||
GitHub,
|
|
||||||
SolarizedDark,
|
|
||||||
SolarizedLight,
|
|
||||||
OceanDark,
|
|
||||||
OceanLight,
|
|
||||||
MagicDark,
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use platform_dirs::{AppDirs, AppUI};
|
|
||||||
use sha2::Digest;
|
|
||||||
use std::fs;
|
|
||||||
use std::io;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CacheStorage {
|
|
||||||
location: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CacheStorage {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
lazy_static::lazy_static! {
|
|
||||||
static ref APP_DIRS: AppDirs = AppDirs::new(Some("snekdown"), AppUI::CommandLine).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
location: APP_DIRS.cache_dir.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the cache path for a given file
|
|
||||||
pub fn get_file_path(&self, path: &PathBuf) -> PathBuf {
|
|
||||||
let mut hasher = sha2::Sha256::default();
|
|
||||||
hasher.update(path.to_string_lossy().as_bytes());
|
|
||||||
let mut file_name = PathBuf::from(format!("{:x}", hasher.finalize()));
|
|
||||||
|
|
||||||
if let Some(extension) = path.extension() {
|
|
||||||
file_name.set_extension(extension);
|
|
||||||
}
|
|
||||||
log::trace!("Cache path is {:?}", path);
|
|
||||||
|
|
||||||
return self.location.join(PathBuf::from(file_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns if the given file exists in the cache
|
|
||||||
pub fn has_file(&self, path: &PathBuf) -> bool {
|
|
||||||
let cache_path = self.get_file_path(path);
|
|
||||||
|
|
||||||
cache_path.exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes into the corresponding cache file
|
|
||||||
pub fn read(&self, path: &PathBuf) -> io::Result<Vec<u8>> {
|
|
||||||
let cache_path = self.get_file_path(path);
|
|
||||||
|
|
||||||
fs::read(cache_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the corresponding cache file
|
|
||||||
pub fn write<R: AsRef<[u8]>>(&self, path: &PathBuf, contents: R) -> io::Result<()> {
|
|
||||||
let cache_path = self.get_file_path(path);
|
|
||||||
|
|
||||||
fs::write(cache_path, contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears the cache directory by deleting and recreating it
|
|
||||||
pub fn clear(&self) -> io::Result<()> {
|
|
||||||
fs::remove_dir_all(&self.location)?;
|
|
||||||
fs::create_dir(&self.location)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,249 +0,0 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use crate::elements::Metadata;
|
|
||||||
use crate::utils::caching::CacheStorage;
|
|
||||||
use crate::utils::downloads::download_path;
|
|
||||||
use image::imageops::FilterType;
|
|
||||||
use image::io::Reader as ImageReader;
|
|
||||||
use image::{GenericImageView, ImageFormat, ImageResult};
|
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
|
||||||
use mime::Mime;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::io;
|
|
||||||
use std::io::Cursor;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ImageConverter {
|
|
||||||
images: Vec<Arc<Mutex<PendingImage>>>,
|
|
||||||
target_format: Option<ImageFormat>,
|
|
||||||
target_size: Option<(u32, u32)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageConverter {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
images: Vec::new(),
|
|
||||||
target_format: None,
|
|
||||||
target_size: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_target_size(&mut self, target_size: (u32, u32)) {
|
|
||||||
self.target_size = Some(target_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_target_format(&mut self, target_format: ImageFormat) {
|
|
||||||
self.target_format = Some(target_format);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds an image to convert
|
|
||||||
pub fn add_image(&mut self, path: PathBuf) -> Arc<Mutex<PendingImage>> {
|
|
||||||
let image = Arc::new(Mutex::new(PendingImage::new(path)));
|
|
||||||
self.images.push(image.clone());
|
|
||||||
|
|
||||||
image
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts all images
|
|
||||||
pub fn convert_all(&mut self) {
|
|
||||||
let pb = Arc::new(Mutex::new(ProgressBar::new(self.images.len() as u64)));
|
|
||||||
pb.lock().set_style(
|
|
||||||
ProgressStyle::default_bar()
|
|
||||||
.template("Processing images: [{bar:40.cyan/blue}]")
|
|
||||||
.progress_chars("=> "),
|
|
||||||
);
|
|
||||||
self.images.par_iter().for_each(|image| {
|
|
||||||
let mut image = image.lock();
|
|
||||||
if let Err(e) = image.convert(self.target_format.clone(), self.target_size.clone()) {
|
|
||||||
log::error!("Failed to embed image {:?}: {}", image.path, e)
|
|
||||||
}
|
|
||||||
pb.lock().tick();
|
|
||||||
});
|
|
||||||
pb.lock().finish_and_clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PendingImage {
|
|
||||||
pub path: PathBuf,
|
|
||||||
pub data: Option<Vec<u8>>,
|
|
||||||
cache: CacheStorage,
|
|
||||||
pub mime: Mime,
|
|
||||||
brightness: Option<i32>,
|
|
||||||
contrast: Option<f32>,
|
|
||||||
huerotate: Option<i32>,
|
|
||||||
grayscale: bool,
|
|
||||||
invert: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PendingImage {
|
|
||||||
pub fn new(path: PathBuf) -> Self {
|
|
||||||
let mime = get_mime(&path);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
path,
|
|
||||||
data: None,
|
|
||||||
cache: CacheStorage::new(),
|
|
||||||
mime,
|
|
||||||
brightness: None,
|
|
||||||
contrast: None,
|
|
||||||
grayscale: false,
|
|
||||||
invert: false,
|
|
||||||
huerotate: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assign_from_meta<M: Metadata>(&mut self, meta: &M) {
|
|
||||||
if let Some(brightness) = meta.get_integer("brightness") {
|
|
||||||
self.brightness = Some(brightness as i32);
|
|
||||||
}
|
|
||||||
if let Some(contrast) = meta.get_float("contrast") {
|
|
||||||
self.contrast = Some(contrast as f32);
|
|
||||||
}
|
|
||||||
if let Some(huerotate) = meta.get_float("huerotate") {
|
|
||||||
self.huerotate = Some(huerotate as i32);
|
|
||||||
}
|
|
||||||
self.grayscale = meta.get_bool("grayscale");
|
|
||||||
self.invert = meta.get_bool("invert");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the image to the specified target format (specified by target_extension)
|
|
||||||
pub fn convert(
|
|
||||||
&mut self,
|
|
||||||
target_format: Option<ImageFormat>,
|
|
||||||
target_size: Option<(u32, u32)>,
|
|
||||||
) -> ImageResult<()> {
|
|
||||||
let format = target_format
|
|
||||||
.or_else(|| {
|
|
||||||
self.path
|
|
||||||
.extension()
|
|
||||||
.and_then(|extension| ImageFormat::from_extension(extension))
|
|
||||||
})
|
|
||||||
.unwrap_or(ImageFormat::Png);
|
|
||||||
|
|
||||||
let output_path = self.get_output_path(format, target_size);
|
|
||||||
self.mime = get_mime(&output_path);
|
|
||||||
|
|
||||||
if self.cache.has_file(&output_path) {
|
|
||||||
self.data = Some(self.cache.read(&output_path)?)
|
|
||||||
} else {
|
|
||||||
self.convert_image(format, target_size)?;
|
|
||||||
|
|
||||||
if let Some(data) = &self.data {
|
|
||||||
self.cache.write(&output_path, data)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the image
|
|
||||||
fn convert_image(
|
|
||||||
&mut self,
|
|
||||||
format: ImageFormat,
|
|
||||||
target_size: Option<(u32, u32)>,
|
|
||||||
) -> ImageResult<()> {
|
|
||||||
let mut image = ImageReader::open(self.get_path()?)?.decode()?;
|
|
||||||
|
|
||||||
if let Some((width, height)) = target_size {
|
|
||||||
let dimensions = image.dimensions();
|
|
||||||
|
|
||||||
if dimensions.0 > width || dimensions.1 > height {
|
|
||||||
image = image.resize(width, height, FilterType::Lanczos3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(brightness) = self.brightness {
|
|
||||||
image = image.brighten(brightness);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(contrast) = self.contrast {
|
|
||||||
image = image.adjust_contrast(contrast);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(rotate) = self.huerotate {
|
|
||||||
image = image.huerotate(rotate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.grayscale {
|
|
||||||
image = image.grayscale();
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.invert {
|
|
||||||
image.invert();
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = Vec::new();
|
|
||||||
let mut writer = Cursor::new(data);
|
|
||||||
|
|
||||||
image.write_to(&mut writer, format)?;
|
|
||||||
self.data = Some(writer.into_inner());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the path of the file
|
|
||||||
fn get_path(&self) -> io::Result<PathBuf> {
|
|
||||||
if !self.path.exists() {
|
|
||||||
if self.cache.has_file(&self.path) {
|
|
||||||
return Ok(self.cache.get_file_path(&self.path));
|
|
||||||
}
|
|
||||||
if let Some(data) = download_path(self.path.to_string_lossy().to_string()) {
|
|
||||||
self.cache.write(&self.path, data)?;
|
|
||||||
return Ok(self.cache.get_file_path(&self.path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(self.path.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the output file name after converting the image
|
|
||||||
fn get_output_path(
|
|
||||||
&self,
|
|
||||||
target_format: ImageFormat,
|
|
||||||
target_size: Option<(u32, u32)>,
|
|
||||||
) -> PathBuf {
|
|
||||||
let mut path = self.path.clone();
|
|
||||||
let mut file_name = path.file_stem().unwrap().to_string_lossy().to_string();
|
|
||||||
let extension = target_format.extensions_str()[0];
|
|
||||||
let type_name = format!("{:?}", target_format);
|
|
||||||
|
|
||||||
if let Some(target_size) = target_size {
|
|
||||||
file_name += &*format!("-w{}-h{}", target_size.0, target_size.1);
|
|
||||||
}
|
|
||||||
if let Some(b) = self.brightness {
|
|
||||||
file_name += &*format!("-b{}", b);
|
|
||||||
}
|
|
||||||
if let Some(c) = self.contrast {
|
|
||||||
file_name += &*format!("-c{}", c);
|
|
||||||
}
|
|
||||||
if let Some(h) = self.huerotate {
|
|
||||||
file_name += &*format!("-h{}", h);
|
|
||||||
}
|
|
||||||
file_name += &*format!("{}-{}", self.invert, self.grayscale);
|
|
||||||
|
|
||||||
file_name += format!("-{}", type_name).as_str();
|
|
||||||
path.set_file_name(file_name);
|
|
||||||
path.set_extension(extension);
|
|
||||||
|
|
||||||
path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mime(path: &PathBuf) -> Mime {
|
|
||||||
let mime = mime_guess::from_ext(
|
|
||||||
path.clone()
|
|
||||||
.extension()
|
|
||||||
.and_then(|e| e.to_str())
|
|
||||||
.unwrap_or("png"),
|
|
||||||
)
|
|
||||||
.first()
|
|
||||||
.unwrap_or(mime::IMAGE_PNG);
|
|
||||||
mime
|
|
||||||
}
|
|
@ -1,11 +1,3 @@
|
|||||||
/*
|
|
||||||
* Snekdown - Custom Markdown flavour and parser
|
|
||||||
* Copyright (C) 2021 Trivernis
|
|
||||||
* See LICENSE for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub mod caching;
|
|
||||||
pub mod downloads;
|
pub mod downloads;
|
||||||
pub mod image_converting;
|
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
|
Loading…
Reference in New Issue