Compare commits

..

82 Commits

Author SHA1 Message Date
675b9d0245 Rebased Terminal Watchface changes 2024-12-15 17:43:33 -06:00
Victor Kareh
d69cfcfb13 weather: Fix inverted imperial forecast temperatures
When converting to imperial units, the min and max temperatures were
incorrectly inverted, causing confusion in the display.

Fixes https://github.com/InfiniTimeOrg/InfiniTime/issues/2183
2024-12-10 00:02:17 +01:00
Felipe Martínez
b8c51abe69
Use all free RAM for FreeRTOS heap
* Use all free RAM for FreeRTOS heap
* Wrap newlib malloc and related functions
* Implement calloc
2024-12-09 00:10:09 +00:00
Jean-François Milants
2105a7b63d Set version to 1.15.0 2024-12-03 20:11:08 +01:00
Lionel Elie Mamane
79ee886904 spelling 2024-11-25 08:55:37 +01:00
Lionel Elie Mamane
b1d70ae2ed remove unused include 2024-11-25 08:55:37 +01:00
xz-dev
a77a3dcb8b cmake: fix python path with use multi-version python 2024-11-17 23:36:22 +00:00
mark9064
8aefa3b9a6 Ignore old GoToRunning messages 2024-11-17 15:35:15 +01:00
febrezo
6c7eb6630e Fix git hash calculation with Docker build
Fix error when compiling the Pinetime using the Docker image.
If done with Docker, the container does not trust the /sources
folder, leading to a blank response of the command that grabs
the git commit `git rev-parse --short HEAD`.

```
fatal: detected dubious ownership in repository at '/sources'
To add an exception for this directory, call:

        git config --global --add safe.directory /sources
PROJECT_GIT_COMMIT_HASH_SUCCESS? 128

BUILD CONFIGURATION
-------------------
    * Mode : Release
    * Version : 1.3.0
    * Toolchain : /opt/gcc-arm-none-eabi-10.3-2021.10
    * GitRef(S) :
    * NRF52 SDK : /opt/nRF5_SDK_15.3.0_59ac345
    * Target device : PINETIME
    * Build DFU (using adafruit-nrfutil) : Enabled
    * Build resources : Enabled
```

If the `git config --global --add safe.directory /sources` is
added to the Dockerfile, the problem is solved and the hash is
added correctly.
2024-11-17 15:22:22 +01:00
Dom Rodriguez
4dd0d60eeb ci: Normalise slash-containing github.head_ref values
This fixes CI on #2121.

Signed-off-by: Dom Rodriguez <shymega@shymega.org.uk>
2024-11-17 15:13:38 +01:00
Jozef Mlich
a2ced5659d Update Amazfish details in README.md
The openrepos contain obsolete version (description on the openrepos page says that). New SailfishOS version is in chum repo. Additionally, there is Ubuntu Touch and Flatpak version.
2024-11-17 00:10:20 +00:00
Lionel Elie Mamane
5ea9c5537e Weather: fix min and max temperature being switched 2024-11-16 23:53:33 +00:00
FintasticMan
f7c87a700d weather: Switch to std::optional for Forecast days
Also only iterate over the number of days actually in use, rather than
MaxNbForecastDays.
2024-11-04 21:22:38 +01:00
FintasticMan
e247bd7019 Switch to simpler temperature interface 2024-11-04 21:22:38 +01:00
FintasticMan
29ad09f4ef weather: Refactor temperature type for type safety
There is now a Temperature struct in the weather service, which holds
the internal representation. There is also a temperature struct in the
Applications namespace, which holds the temperature in either Celsius or
Fahrenheit.
2024-11-04 21:22:38 +01:00
Samuel Dorsaz
afeded0126 Update Date&Time settings label for "Date & Time" 2024-11-04 21:20:44 +01:00
mark9064
57b6db8b2a Remove OnTouchEvent 2024-10-27 19:51:01 +01:00
mark9064
0076962588 Unify touch panel handling 2024-10-27 19:51:01 +01:00
mark9064
e6ee548536 Process touch events only when awake 2024-10-27 19:51:01 +01:00
mark9064
1808634f0e Clear ongoing taps when going to sleep 2024-10-27 19:51:01 +01:00
Lionel Elie Mamane
cfaad261dc SimpleWeatherService #include <array>
not <vector> as that is what is actually used.
Fixes build failure
InfiniTime/src/components/ble/SimpleWeatherService.h:86:18: error: field ‘location’ has incomplete type ‘Pinetime::Controllers::SimpleWeatherService::Location’ {aka ‘std::array<char, 33>’
2024-10-27 18:27:03 +01:00
FintasticMan
f1651c8000 datetime: Set the default year to the year during compile 2024-10-27 17:01:07 +01:00
mark9064
8a2ee437f5 Restrict hardware reactivation when not sleeping 2024-10-27 16:56:47 +01:00
mark9064
06b721a71f Improve sleep time calculation docs 2024-10-27 16:56:47 +01:00
mark9064
771008495e Replace rounded div macro 2024-10-27 16:56:47 +01:00
mark9064
f032847ae1 Refactor into defined states 2024-10-27 16:56:47 +01:00
mark9064
97ba39988b 8hz idle 2024-10-27 16:56:47 +01:00
liamcharger
879bdccd92
README: Header redesign (#2032)
* Reformat README.md

* Update README.md

* Add files via upload

* Add files via upload

* Add new README.md header image

* Remove unnecessary <br> tag

* Scale watchface logo down; add downloads badge

* Remove unnecessary <br> tag

* Add <br> tag to maintain spacing consistency

* Remove incorrect link

* Add watchface logo with dark logo

* Replace logo image with dark version

* Add files via upload

* Add header image

* Fix row spacing in header image

* Remove unnecessary <br> tag

* Add corner radius to header image

* Add files via upload

* Update image background

* Add reference to InfiniTimeExplorer

* Remove unnecessary image

* Remove unnecessary image

* Remove unnecessary images

* Rename watchface_collage_no_shadow.png to watchface_collage.png

* Update header image URL

* Remove unnecessary <br> tag

* SPI transaction hooks

* Remove task to notify

* Refactor display WriteToRam

* Use functional abstraction for hooks

* Refactor lambdas

* Avoid storing lambda

* Rename to pre-transaction hook

* Use FreeRTOS delay instead of spinning the CPU

* Apply display driver datasheet delays

* Move includes back

* Include task header (Fixes sim)

* Make chime vibrate twice

* Remove commit from main

* README: improve wording

* Add bootloader to DeviceInformationService.cpp

* Add bootloader to DeviceInformationService.h

* Revert "Add bootloader to DeviceInformationService.h"

This reverts commit f3f0fd568d96dea1ebd30529b4b8b6d5f4d5444b.

* Revert "Add bootloader to DeviceInformationService.cpp"

This reverts commit 35570edafab1b061442ae89b01a2ced8b9a812bd.

* Delete doc/logo/watchface_collage.png

* Add files via upload

* Delete doc/logo/watchface_collage.png

* Add files via upload

* Replace collage with correct image

---------

Co-authored-by: mark9064 <30447455+mark9064@users.noreply.github.com>
2024-10-23 19:00:48 +02:00
NeroBurner
8598142c27
Remove unused submodule QCBOR (#2138)
The submodule isn't used anymore. Remove the submodule reference
completely.
2024-10-09 20:26:08 +02:00
NeroBurner
a2356f2f4a
MusicService: add missing includes for TickType_t and xTaskGetTickCount (#2130)
Add `FreeRTOS.h` include for the directly used data type `TickType_t` in the header
and the function `xTaskGetTickCount` from FreeRTOS's `task.h`
2024-09-29 21:10:32 +02:00
NeroBurner
3db4e012ce
Remove unused pointer to DisplayApp member variables (#2125)
In the screens that use `DisplayApp *app` and pass it to a child item,
or use the reference just in the constructor. Afterwards the `app`
member is not used. So remove it from the private member variables.

Completely remove `app` parameter from `SettingDisplay` constructor as
it is unused.
2024-09-29 19:39:14 +02:00
NeroBurner
a0cd439efc
Alarm persist to flash (#1367)
* AlarmController: Add saving alarm time to file

Save the set alarm time to the SPI NOR flash, so it does not reset to
the default value when the watch resets, e.g. due to watchdog timeout
or reflashing of a new version of InfiniTime.

Just like the `Settings.h` `LoadSettingsFromFile()` the previous alarm
at boot (if available) and `SaveSettingsToFile()` the current alarm when
the `Alarm.h` screen is closed (only if the settings have changed).

The alarm-settings file is stored in `.system/alarm.dat`. The `.system`
folder is created if it doesn't yet exist.

Fixes: https://github.com/InfiniTimeOrg/InfiniTime/issues/1330

* alarmController: close .system dir after usage

Close the `lfs_dir` object for the `.system` dir after usage. Otherwise
on the second changed alarm the system will lockup because the `.system`
dir is already open and was never closed.

---------

Co-authored-by: Galdor Takacs <g@ldor.de>
2024-09-28 08:14:08 +02:00
Reinhold Gschweicher
997e4cee8c Hrs3300: fix includes for std::begin/std::end
Fix for Hrs3300 PR about Atomic HRS reads:
https://github.com/InfiniTimeOrg/InfiniTime/pull/1845

We use `std::begin` and `std::end`, but we don't include one of the
headers that define those functions.
See https://en.cppreference.com/w/cpp/iterator/begin for a list of
headers that define `std::begin` and `std::end`.

Starting with GCC 14 this leads to a compilation error presumably
because they cleaned up their headers.

Fix code by inlcuding `<iterator>`
2024-09-22 16:15:48 +02:00
mark9064
ad3bf49c7b
Atomic HRS reads (#1845)
- Combine the reading of all `HRS3300` registers into one I2C read so data is not partial
- Downsizes both HRS and ALS to 16bit as the sensor does not generate larger than
  16bit values in its current configuration
  - Increasing the resolution by 1 bit doubles the sensor acquisition time,
    since we are already at 10Hz we are never going to use a higher resolution
  - The PPG algorithm buffers for ALS/HRS are already 16bit anyway
- Remove functions for setting gain / drive that are unused throughout the codebase
- Calculate constants with constexpr
2024-09-22 00:29:15 +02:00
mark9064
7ca0418c82 Refactor doNotGoToSleep to a wakelock counter 2024-09-21 22:45:57 +02:00
mark9064
c3d05901a0 Refactor SystemTask state handling for resilience
State transitions now happen immediately where possible
This simplifies state management in general,
and prevents bugs such as the chime issue from occurring in the first place
2024-09-21 21:08:07 +02:00
mark9064
b3756e45fa Remove unused method declarations 2024-09-21 21:08:07 +02:00
Eli Tan
a266202831
notifications: Dismiss to watchface when empty (#1716)
Set `running` to false to flag end of watchface when there are no more
notifications left to display.

I found it slightly annoying that dismissing all notifications leaves me with
a "No notification to display" message. Instead of dismissing to a relatively
useless message, dismiss to watchface.
2024-09-18 22:31:15 +02:00
mark9064
c8236afbef Restrict debugging monitor to debug builds 2024-09-14 12:10:55 +02:00
mark9064
5040733a97 Clean unused DisplayApp messages 2024-09-14 12:10:14 +02:00
mark9064
fd019c7aad Use DirtyValue for timer 2024-09-14 11:20:47 +02:00
mark9064
975bfc5420 Size optimise NRF SDK build 2024-09-14 11:19:34 +02:00
Victor Kareh
2625ed39e5 DisplayApp: Go to clock on sleep if no app loaded
When turning off the screen, if there is no actual app loaded (i.e. we
are still in the Launcher, Notifications, QuickSettings, or Settings
screens) we should just reload the Clock app directly.
2024-08-22 17:34:25 +02:00
Derry Tutt
a3dbcd62f6
Documentation improvements (#2091)
Add documentation about watch faces and applications.
Update getting started documentation.

Co-authored-by: tituscmd <154823939+tituscmd@users.noreply.github.com>
2024-08-18 16:18:15 +02:00
mark9064
4fddf93114 Advertise HR service 2024-08-18 12:00:13 +02:00
Derry Tutt
83922fb3de Remove space before colon 2024-08-18 11:58:56 +02:00
Felipe Martínez
95917c65a5 Update main.yml 2024-08-18 11:55:03 +02:00
Jean-François Milants
3a0d673df4 Display the SPI flash JEDEC IDs in SystemInformation.
This is needed since a new memory chip will be used in future batches of PineTimes.
2024-08-05 20:34:41 +02:00
mark9064
53dc9dafe7 aod: simplify AOD disablement based on notification status 2024-08-05 20:32:43 +02:00
mark9064
a407902b06 aod: avoid spinning DisplayApp under high LVGL load 2024-08-05 20:32:43 +02:00
mark9064
3e8accde69 aod: run LVGL task handler until all work finished 2024-08-05 20:32:43 +02:00
mark9064
2bb611db8e aod: constant frequency idle frames 2024-08-05 20:32:43 +02:00
mark9064
ef88e8165c aod: porch control: 2Hz idle + 75Hz on 2024-08-05 20:32:43 +02:00
mark9064
da9ab4a7b4 aod: lower lcd voltage 2024-08-05 20:32:43 +02:00
John Crawford
0bcd7e0009 aod: lower voltage going to the display 2024-08-05 20:32:43 +02:00
mark9064
bf69e0dcc5 aod: fix flashlight brightness restore 2024-08-05 20:32:43 +02:00
mark9064
947c4f5067 aod: fix brightness getting stuck high 2024-08-05 20:32:43 +02:00
John Crawford
0960d67001 aod: lower refresh rate when always on 2024-08-05 20:32:43 +02:00
John Crawford
5385f7e275 aod: switch to 8 colors when always on 2024-08-05 20:32:43 +02:00
John Crawford
e884b053d3 aod: disable while in notification sleep 2024-08-05 20:32:43 +02:00
John Crawford
85a2181b64 aod: integrate with display timeout 2024-08-05 20:32:43 +02:00
mark9064
3dca742b65 aod: PPI/RTC-based backlight brightness 2024-08-05 20:32:43 +02:00
KaffeinatedKat
20ac7e8df3 feat: always on display 2024-08-05 20:32:43 +02:00
mark9064
f8f8993fac Batch display command arguments 2024-06-19 17:25:15 +02:00
Max Buchholz
fdc3b8badb README: Change links from Pine wiki to new documentation
As the Wiki was replaced by a new documentation website and is read-only, we should update the links, to point to the most up-to-date documentation.
2024-06-15 12:00:37 +02:00
Jean-François Milants
f9a16feeaf Continuous time updates
Add TODO.md in src/components/datetime. This file give detailed information about a refactoring of the DateTimeController that would be nice to do in the future.
2024-06-09 18:34:07 +02:00
mark9064
4930c0cab7 Include assert (fix sim) 2024-06-09 18:34:07 +02:00
mark9064
a449b272f7 Continuous time updates 2024-06-09 18:34:07 +02:00
Felipe Martínez
9e406c70f9 Remove unnecessary BMA421 reads 2024-06-02 15:48:26 +02:00
Felipe Martinez
0c87bc27b2 Move motorController.Init call to DisplayApp::Start 2024-06-02 15:14:02 +02:00
mark9064
0dcfb2edb7 Fix erratum 58 workaround 2024-05-12 13:57:12 +02:00
mark9064
06c6935315 Include task header (Fixes sim) 2024-05-01 16:17:59 +02:00
mark9064
47c104643d Move includes back 2024-05-01 16:17:59 +02:00
mark9064
7b1110187e Apply display driver datasheet delays 2024-05-01 16:17:59 +02:00
mark9064
7e460d3c80 Use FreeRTOS delay instead of spinning the CPU 2024-05-01 16:17:59 +02:00
mark9064
7a9211587a Rename to pre-transaction hook 2024-05-01 16:13:47 +02:00
mark9064
24e6a2f8ab Avoid storing lambda 2024-05-01 16:13:47 +02:00
mark9064
9a7ba405e1 Refactor lambdas 2024-05-01 16:13:47 +02:00
mark9064
940cd3459f Use functional abstraction for hooks 2024-05-01 16:13:47 +02:00
mark9064
869bec8f88 Refactor display WriteToRam 2024-05-01 16:13:47 +02:00
mark9064
ee925200c3 Remove task to notify 2024-05-01 16:13:47 +02:00
mark9064
079e676baf SPI transaction hooks 2024-05-01 16:13:47 +02:00
249 changed files with 1713 additions and 32723 deletions

View File

@ -46,20 +46,30 @@ jobs:
# Unzip the package because Upload Artifact will zip up the files # Unzip the package because Upload Artifact will zip up the files
- name: Unzip DFU package - name: Unzip DFU package
run: unzip ./build/output/pinetime-mcuboot-app-dfu-*.zip -d ./build/output/pinetime-mcuboot-app-dfu run: unzip ./build/output/pinetime-mcuboot-app-dfu-*.zip -d ./build/output/pinetime-mcuboot-app-dfu
- name: Set ref_name, but replace slashes with dashes.
shell: bash
env:
ref_name: ${{ github.head_ref || github.ref_name }}
run: echo "REF_NAME=${ref_name//\//-}" >> $GITHUB_ENV
- name: Upload DFU artifacts - name: Upload DFU artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: InfiniTime DFU ${{ github.head_ref }} name: InfiniTime DFU ${{ env.REF_NAME }}
path: ./build/output/pinetime-mcuboot-app-dfu/* path: ./build/output/pinetime-mcuboot-app-dfu/*
- name: Upload MCUBoot image artifacts - name: Upload MCUBoot image artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: InfiniTime MCUBoot image ${{ github.head_ref }} name: InfiniTime MCUBoot image ${{ env.REF_NAME }}
path: ./build/output/pinetime-mcuboot-app-image-*.bin path: ./build/output/pinetime-mcuboot-app-image-*.bin
- name: Upload standalone ELF artifacts
uses: actions/upload-artifact@v3
with:
name: InfiniTime image ${{ env.REF_NAME }}
path: ./build/output/src/pinetime-app-*.out
- name: Upload resources artifacts - name: Upload resources artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: InfiniTime resources ${{ github.head_ref }} name: InfiniTime resources ${{ env.REF_NAME }}
path: ./build/output/infinitime-resources-*.zip path: ./build/output/infinitime-resources-*.zip
build-simulator: build-simulator:
@ -100,7 +110,7 @@ jobs:
- name: Upload simulator executable - name: Upload simulator executable
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: infinisim-${{ github.head_ref }} name: infinisim-${{ env.REF_NAME }}
path: build_lv_sim/infinisim path: build_lv_sim/infinisim
get-base-ref-size: get-base-ref-size:

2
.gitignore vendored
View File

@ -11,7 +11,7 @@ cmake_install.cmake
Makefile Makefile
build build
tools tools
node_modules/
# Resulting binary files # Resulting binary files
*.a *.a
*.so *.so

3
.gitmodules vendored
View File

@ -4,9 +4,6 @@
[submodule "src/libs/littlefs"] [submodule "src/libs/littlefs"]
path = src/libs/littlefs path = src/libs/littlefs
url = https://github.com/littlefs-project/littlefs.git url = https://github.com/littlefs-project/littlefs.git
[submodule "src/libs/QCBOR"]
path = src/libs/QCBOR
url = https://github.com/laurencelundblade/QCBOR.git
[submodule "src/libs/arduinoFFT"] [submodule "src/libs/arduinoFFT"]
path = src/libs/arduinoFFT path = src/libs/arduinoFFT
url = https://github.com/kosme/arduinoFFT.git url = https://github.com/kosme/arduinoFFT.git

View File

@ -1,7 +1,54 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="2" />
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
<option name="INDENT_CLASS_MEMBERS" value="2" />
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
<option name="INDENT_DIRECTIVE_AS_CODE" value="true" />
<option name="SPACE_BEFORE_TEMPLATE_DECLARATION_LT" value="true" />
<option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
</Objective-C>
<Objective-C-extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<clangFormatSettings> <clangFormatSettings>
<option name="ENABLED" value="true" /> <option name="ENABLED" value="true" />
</clangFormatSettings> </clangFormatSettings>
<codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="140" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="WRAP_ON_TYPING" value="1" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
<option name="LABEL_INDENT_ABSOLUTE" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme> </code_scheme>
</component> </component>

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
project(pinetime VERSION 1.14.0 LANGUAGES C CXX ASM) project(pinetime VERSION 1.15.0 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)

View File

@ -1,27 +1,50 @@
# [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) <div align="center">
![InfiniTime logo](doc/logo/infinitime-logo-small.jpg "InfiniTime Logo") ![Header Image](doc/logo/watchface_collage.png)
Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++. <br>
[![GitHub tag](https://img.shields.io/github/tag/InfiniTimeOrg/InfiniTime?include_prereleases=&sort=semver&color=blue)](https://github.com/InfiniTimeOrg/InfiniTime/releases)
[![GitHub License](https://img.shields.io/github/license/InfiniTimeOrg/InfiniTime)](https://github.com/InfiniTimeOrg/InfiniLink/blob/main/LICENSE)
[![Issues - InfiniTime](https://img.shields.io/github/issues/InfiniTimeOrg/InfiniTime)](https://github.com/InfiniTimeOrg/InfiniTime/issues)
[![Pull Requests - InfiniTime](https://img.shields.io/github/issues-pr/InfiniTimeOrg/InfiniTime)](https://github.com/InfiniTimeOrg/InfiniTime/pulls)
[![Downloads - InfiniTime](https://img.shields.io/github/downloads/InfiniTimeOrg/InfiniTime/total)](https://github.com/InfiniTimeOrg/InfiniTime)
[![Stars - InfiniTime](https://img.shields.io/github/stars/InfiniTimeOrg/InfiniTime?style=social)](https://github.com/InfiniTimeOrg/InfiniTime/stargazers)
[![Forks - InfiniTime](https://img.shields.io/github/forks/InfiniTimeOrg/InfiniTime?style=social)](https://github.com/InfiniTimeOrg/InfiniTime/network/members)
# InfiniTime
*Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++.*
<br>
</div>
## New to InfiniTime? ## New to InfiniTime?
- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) - [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md)
- [Updating the software](doc/gettingStarted/updating-software.md) - [Updating the software](doc/gettingStarted/updating-software.md)
- [About the firmware and bootloader](doc/gettingStarted/about-software.md) - [About the firmware and bootloader](doc/gettingStarted/about-software.md)
- [PineTimeStyle Watch face](https://wiki.pine64.org/wiki/PineTimeStyle) - [Available apps](doc/gettingStarted/Applications.md)
- [Weather integration](https://wiki.pine64.org/wiki/Infinitime-Weather) - [Available watch faces](/doc/gettingStarted/Watchfaces.md)
- [PineTimeStyle Watch face](https://pine64.org/documentation/PineTime/Watchfaces/PineTimeStyle)
- [Weather integration](https://pine64.org/documentation/PineTime/Software/InfiniTime_weather/)
### Companion apps ### Companion apps
- [Gadgetbridge](https://gadgetbridge.org/) (Android) - [Gadgetbridge](https://gadgetbridge.org/) (Android)
- [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS) - [Amazfish](https://github.com/piggz/harbour-amazfish/) ([SailfishOS](https://sailfishos-chum.github.io/apps/harbour-amazfish/), [Ubuntu Touch](https://open-store.io/app/uk.co.piggz.amazfish), [Flatpak](https://flathub.org/apps/uk.co.piggz.amazfish))
- [Siglo](https://github.com/alexr4535/siglo) (Linux) - [Siglo](https://github.com/alexr4535/siglo) (Linux)
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS) - [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS)
- [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux) - [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux)
- [WatchMate](https://github.com/azymohliad/watchmate) (Linux) - [WatchMate](https://github.com/azymohliad/watchmate) (Linux)
- [InfiniTimeExplorer](https://infinitimeexplorer.netlify.app) (Web)
***Note**: We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).* <br>
> *InfiniTimeExplorer is only compatible with web browsers that support Web BLE. Current fully supported browsers include Chrome and Microsoft Edge.*
>
> *We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).*
## Development ## Development
@ -35,7 +58,7 @@ Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devic
### Contributing ### Contributing
- [How to contribute?](CONTRIBUTING.md) - [How to contribute](CONTRIBUTING.md)
- [Coding conventions](doc/coding-convention.md) - [Coding conventions](doc/coding-convention.md)
### Build, flash and debug ### Build, flash and debug

View File

@ -0,0 +1,99 @@
# Applications
InfiniTime has 13 apps on the `main` branch at the time of writing.
## List of apps
- Stopwatch
- Alarm
- Timer
- Steps
- Heartrate
- Music
- InfiniPaint
- Paddle
- 2
- InfiniDice
- Metronome
- Maps
- Weather
### Stopwatch
![Stopwatch UI](/doc/gettingStarted/AppsScreenshots/stopwatch.png)
- Press the Start button (bottom right) to start or stop the timer.
- You can also press the side button while the timer is running to pause the timer.
- Press the Flag button (bottom left) to add a lap.
- The stopwatch will not yet continue counting time while the app is closed.
### Alarm
![Alarm UI](/doc/gettingStarted/AppsScreenshots/alarm.png)
- Ajust the time with the time picker.
- Press the Info button in the top middle to see time remaning.
- Use the toggle in the bottom left to turn the alarm on/off.
- Use the button in the bottom right to change the alarm frequency.
- You can choose between once, daily, or Monday - Friday.
### Timer
![Timer UI](/doc/gettingStarted/AppsScreenshots/timer.png)
- Ajust how long the timer should go for with the time picker.
- Press the Start button at the bottom to start/stop the timer.
### Steps
![Steps UI](/doc/gettingStarted/AppsScreenshots/steps.png)
- The total count of steps for the current display will show in the middle of the screen.
- The Reset button in the bottom middle resets the Trip counter. (Total of all steps taken.)
- The progress circle shows the percentage of your daily goal completed.
### Heartrate
![Heartrate UI](/doc/gettingStarted/AppsScreenshots/Heartrate.png)
- Press Start to start measuring your heartrate.
- It may take a bit to get the first measurement.
### Music
![Music UI](/doc/gettingStarted/AppsScreenshots/Music.png)
- This app shows currently playing music.
- Please note that this app is not very useful without a device connected.
- Press the button in the center to play/pause, and the buttons on the left and right to go to the previous and next tracks, respectively.
- Swipe up to get to volume controls.
### InfiniPaint
![InfiniPaint UI](/doc/gettingStarted/AppsScreenshots/Paint.png)
- This app does not allow you to swipe from the top to exit, use the side button instead.
- Draw on the screen to add lines.
- Hold down in one spot to change paint colors.
### Paddle
![Paddle UI](/doc/gettingStarted/AppsScreenshots/Pong.png)
- This app does not allow you to swipe from the top to exit, use the side button instead.
- Drag your finger to move the paddle.
- Goal: Don't let the ball go off the left side of the screen.
### 2
![2 UI](/doc/gettingStarted/AppsScreenshots/2048.png)
- This app does not allow you to swipe from the top to exit, use the side button instead.
- Play a game of 2048.
- Swipe up, down, left, or right tomove the tiles.
- When two tiles with the same number run into each other, they will add together.
- Goal: Don't let the screen fill up with tiles, and get to the 2048 tile to win.
### InfiniDice
![InfiniDice UI](/doc/gettingStarted/AppsScreenshots/Dice.png)
- Ajust the count to change the number of dice.
- Ajust the sides to change the number of sides.
- Press the button at the bottom to roll.
- The result will be on the right side of the screen.
### Metronome
![Metronome UI](/doc/gettingStarted/AppsScreenshots/Metronome.png)
- Ajust the BPM with the circular slider.
- A bug currently makes it always snap to 98 BPM.
- Use the button in the bottom left to start the metronome.
### Maps
![Maps UI](/doc/gettingStarted/AppsScreenshots/Maps.png)
- This app shows info from a navigation app.
- Please note that this app is not very useful without a device connected.
### Weather
![Weather UI](/doc/gettingStarted/AppsScreenshots/Weather.png)
- This app shows weather info.
- Please note that this app is not very useful without a device connected.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,31 @@
# Watchfaces
InfiniTime has 6 apps on the `main` branch at the time of writing.
## List of apps
- Digital
- Analog
- PineTimeStyle
- Terminal
- Infinineat
- Casio G7710
### Digital
![Digital face](/doc/gettingStarted/Watchfaces/Digital.png)
### Analog
![Analog face](/doc/gettingStarted/Watchfaces/Analog.png)
### PineTimeStyle
![PineTimeStyle face](/doc/gettingStarted/Watchfaces/PineTimeStyle.png)
- You can long-press on the display to change colors, step style, and weather.
### Terminal
![Terminal face](/doc/gettingStarted/Watchfaces/Terminal.png)
### Infinineat
![Infinineat face](/doc/gettingStarted/Watchfaces/Infinineat.png)
- You can long-press on the display to change colors.
### Casio G7710
![Casio G7710 face](/doc/gettingStarted/Watchfaces/CasioG7710.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 256 KiB

View File

@ -14,7 +14,7 @@ You can sync the time using companion apps.
- Gadgetbridge automatically synchronizes the time when you connect it to your watch. More information on Gadgetbridge [here](/doc/gettingStarted/ota-gadgetbridge.md) - Gadgetbridge automatically synchronizes the time when you connect it to your watch. More information on Gadgetbridge [here](/doc/gettingStarted/ota-gadgetbridge.md)
- [Sync the time with NRFConnect](/doc/gettingStarted/time-nrfconnect.md) - [Sync the time with NRFConnect](/doc/gettingStarted/time-nrfconnect.md)
- Sync the time with your browser https://hubmartin.github.io/WebBLEWatch/ - [Sync the time with your browser](https://hubmartin.github.io/WebBLEWatch/)
You can also set the time in the settings without a companion app. (version >1.7.0) You can also set the time in the settings without a companion app. (version >1.7.0)
@ -46,7 +46,7 @@ On the bottom right, you can see how many steps you have taken today.
![Settings](ui/settings.jpg) ![Settings](ui/settings.jpg)
- Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu. - Swipe **up** to display the application menus. Apps (stopwatch, music, step, games,...) can be started from this menu.
- Swipe **down** to display the notification panel. Notification sent by your companion app will be displayed here. - Swipe **down** to display the notification panel. Notifications sent by your companion app will be displayed here.
- Swipe **right** to display the Quick Actions menu. This menu allows you to - Swipe **right** to display the Quick Actions menu. This menu allows you to
- Set the brightness of the display - Set the brightness of the display
- Start the **flashlight** app - Start the **flashlight** app

View File

@ -1,29 +1,35 @@
# Connecting to Gadgetbridge # Connecting to Gadgetbridge
Launch Gadgetbridge and tap on the **"+"** button on the bottom right to add a new device: Launch Gadgetbridge and tap on the menu button in the top left:
![Gadgetbridge 0](gadgetbridge0.jpg) ![Gadgetbridge 0](gadgetbridge0.jpg)
Wait for the scan to complete, your PineTime should be detected: Press the "Connect new device" button:
![Gadgetbridge 1](gadgetbridge1.jpg) ![Gadgetbridge 1](gadgetbridge1.jpg)
Your PineTime should appear on the list. Tap on it.
Tap on it. Gadgdetbridge will pair and connect to your device: Tap on it. Gadgdetbridge will pair and connect to your device:
![Gadgetbridge 2](gadgetbridge2.jpg) ![Gadgetbridge 2](gadgetbridge2.jpg)
# Updating with Gadgetbridge # Updating with Gadgetbridge
Now that Gadgetbridge is connected to your PineTime, use a file browser application and find the DFU file (`pinetime-mcuboot-app-dfu-x.x.x.zip`) you downloaded previously. Tap on it and open it using the Gadgetbridge application/firmware installer: Now that Gadgetbridge is connected to your PineTime, press the three dots on the device card:
![Gadgetbridge 3](gadgetbridge3.jpg) ![Gadgetbridge 3](gadgetbridge3.jpg)
Read the warning carefully and tap **Install**: Now press the "File Installer" button:
![Gadgetbridge 4](gadgetbridge4.jpg) ![Gadgetbridge 4](gadgetbridge4.jpg)
Wait for the transfer to finish. Your PineTime should reset and reboot with the new version of InfiniTime! Select the firmware you downloaded (`pinetime-mcuboot-app-dfu-x.x.x.zip`) from the [Releases tab](https://github.com/InfiniTimeOrg/InfiniTime/releases/latest):
Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and Firmware option and click **validate**. Otherwise after reboot the previous firmware will be used.
![Gadgetbridge 5](gadgetbridge5.jpg) ![Gadgetbridge 5](gadgetbridge5.jpg)
Wait for the transfer to finish. There will be a progress bar on both the watch and the phone. Your PineTime should reboot with the new version of InfiniTime!
Don't forget to **validate** your firmware. In the InfiniTime go to the settings (swipe right, select gear icon) and scroll to the Firmware option and click **validate**. Otherwise, after reboot the previous firmware will be used.
![Validate](validate.png)

View File

@ -6,7 +6,7 @@ If you just want to flash or upgrade InfiniTime on your PineTime, this page is f
You can check the InfiniTime version by first swiping right on the watch face to open quick settings, tapping the cogwheel to open settings, swipe up until you find an entry named "About" and tap on it. You can check the InfiniTime version by first swiping right on the watch face to open quick settings, tapping the cogwheel to open settings, swipe up until you find an entry named "About" and tap on it.
![InfiniTime 1.0 version](version-1.0.jpg) ![InfiniTime 1.14 version](version.png)
PineTimes shipped after June 2021 will ship with the latest version of [the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/tag/1.0.0) and [recovery firmware](https://github.com/InfiniTimeOrg/InfiniTime/releases/tag/0.14.1) PineTimes shipped after June 2021 will ship with the latest version of [the bootloader](https://github.com/JF002/pinetime-mcuboot-bootloader/releases/tag/1.0.0) and [recovery firmware](https://github.com/InfiniTimeOrg/InfiniTime/releases/tag/0.14.1)
@ -49,7 +49,7 @@ Since those resources are not part of the firmware, they need to be flashed and
Resources are packaged into a single .zip file named `infinitime-resources-x.y.z.zip` (where `x`, `y` and `z` are the version numbers of InfiniTime). Resources are packaged into a single .zip file named `infinitime-resources-x.y.z.zip` (where `x`, `y` and `z` are the version numbers of InfiniTime).
You can use the companion app of your choice to flash the resources. You can use the companion app of your choice to flash the resources.
**Note : at the time of writing this page, [Amazfish](https://github.com/piggz/harbour-amazfish) and [ITD](https://gitea.arsenm.dev/Arsen6331/itd) have already integrated this functionality. Other companion apps will hopefully implement it soon!* **Note: at the time of writing this page, [Amazfish](https://github.com/piggz/harbour-amazfish) and [ITD](https://gitea.arsenm.dev/Arsen6331/itd) have already integrated this functionality. Other companion apps will hopefully implement it soon!*
## Amazfish ## Amazfish
Use the `Download file` functionality of Amazfish. Use the `Download file` functionality of Amazfish.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 KiB

View File

@ -64,6 +64,9 @@ RUN bash -c "source /opt/build.sh; GetMcuBoot;"
# Add the infinitime user for connecting devcontainer # Add the infinitime user for connecting devcontainer
RUN adduser infinitime RUN adduser infinitime
# Configure Git to accept the /sources directory as safe
RUN git config --global --add safe.directory /sources
ENV SOURCES_DIR /sources ENV SOURCES_DIR /sources
CMD ["/opt/build.sh"] CMD ["/opt/build.sh"]

113
node_modules/.package-lock.json generated vendored
View File

@ -1,113 +0,0 @@
{
"name": "InfiniTime",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/lv_font_conv": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/lv_font_conv/-/lv_font_conv-1.5.2.tgz",
"integrity": "sha512-0UapRSTkVP/pnB8Z4r2HDHx5p2dJx/xUG1+14u/WXo59mwuC7BahR+Bnx/66jKoDrG1wFQwn9ZzoyMxRHOD9bg==",
"bundleDependencies": [
"argparse",
"bit-buffer",
"debug",
"make-error",
"mkdirp",
"opentype.js",
"pngjs"
],
"dependencies": {
"argparse": "^2.0.0",
"bit-buffer": "^0.2.5",
"debug": "^4.1.1",
"make-error": "^1.3.5",
"mkdirp": "^1.0.4",
"opentype.js": "^1.1.0",
"pngjs": "^6.0.0"
},
"bin": {
"lv_font_conv": "lv_font_conv.js"
}
},
"node_modules/lv_font_conv/node_modules/argparse": {
"version": "2.0.1",
"inBundle": true,
"license": "Python-2.0"
},
"node_modules/lv_font_conv/node_modules/bit-buffer": {
"version": "0.2.5",
"inBundle": true,
"license": "MIT"
},
"node_modules/lv_font_conv/node_modules/debug": {
"version": "4.3.1",
"inBundle": true,
"license": "MIT",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/lv_font_conv/node_modules/make-error": {
"version": "1.3.6",
"inBundle": true,
"license": "ISC"
},
"node_modules/lv_font_conv/node_modules/mkdirp": {
"version": "1.0.4",
"inBundle": true,
"license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/lv_font_conv/node_modules/ms": {
"version": "2.1.2",
"inBundle": true,
"license": "MIT"
},
"node_modules/lv_font_conv/node_modules/opentype.js": {
"version": "1.3.3",
"inBundle": true,
"license": "MIT",
"dependencies": {
"string.prototype.codepointat": "^0.2.1",
"tiny-inflate": "^1.0.3"
},
"bin": {
"ot": "bin/ot"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/lv_font_conv/node_modules/pngjs": {
"version": "6.0.0",
"inBundle": true,
"license": "MIT",
"engines": {
"node": ">=12.13.0"
}
},
"node_modules/lv_font_conv/node_modules/string.prototype.codepointat": {
"version": "0.2.1",
"inBundle": true,
"license": "MIT"
},
"node_modules/lv_font_conv/node_modules/tiny-inflate": {
"version": "1.0.3",
"inBundle": true,
"license": "MIT"
}
}
}

View File

@ -1,164 +0,0 @@
1.5.2 / 2021-07-18
------------------
- Fixed lvgl version check for v8+, #64.
1.5.1 / 2021-04-06
------------------
- Fixed fail of CMAP generation for edge cases, #62.
- Dev deps bump.
1.5.0 / 2021-03-08
------------------
- More `const` in generated font (for v8+), #59.
1.4.1 / 2021-01-26
------------------
- Fix charcodes padding in comments, #54.
1.4.0 / 2021-01-03
------------------
- Added OTF fonts support.
- Added `--use-color-info` for limited multi-tone glyphs support.
1.3.1 / 2020-12-28
------------------
- Unify `lvgl.h` include.
- Updated repo refs (littlevgl => lvgl).
- Deps bump.
- Moved CI to github actions.
1.3.0 / 2020-10-25
------------------
- Drop `lodash` use.
- Deps bump.
1.2.1 / 2020-10-24
------------------
- Reduced npm package size (drop unneeded files before publish).
1.2.0 / 2020-10-24
------------------
- Bump FreeType to 2.10.4.
- Bundle dependencies to npm package.
1.1.3 / 2020-09-22
------------------
- lvgl: added `LV_FONT_FMT_TXT_LARGE` check or very large fonts.
1.1.2 / 2020-08-23
------------------
- Fix: skip `glyph.advanceWidth` for monospace fonts, #43.
- Spec fix: version size should be 4 bytes, #44.
- Spec fix: bbox x/y bits => unsigned, #45.
- Bump argparse.
- Cleanup help formatter.
1.1.1 / 2020-08-01
------------------
- `--version` should show number from `package.json`.
1.1.0 / 2020-07-27
------------------
- Added `post.underlinePosition` & `post.underlineThickness` info to font header.
1.0.0 / 2020-06-26
------------------
- Maintenance release.
- Set package version 1.x, to label package as stable.
- Deps bump.
0.4.3 / 2020-03-05
------------------
- Enabled `--bpp 8` mode.
0.4.2 / 2020-01-05
------------------
- Added `--lv_include` option to set alternate `lvgl.h` path.
- Added guards to hide `.subpx` property for lvgl 6.0 (supported from 6.1 only), #32.
- Dev deps bump
0.4.1 / 2019-12-09
------------------
- Allow memory growth for FreeType build, #29.
- Dev deps bump.
- Web build update.
0.4.0 / 2019-11-29
------------------
- Note, this release is for lvgl 6.1 and has potentially breaking changes
(see below). If you have compatibility issues with lvgl 6.0 - use previous
versions or update your code.
- Spec change: added subpixels info field to font header (header size increased).
- Updated `bin` & `lvgl` writers to match new spec.
- lvgl: fixed data type for kerning values (needs appropriate update
in LittlevGL 6.1+).
- Fix errors display (disable emscripten error catcher).
0.3.1 / 2019-10-24
------------------
- Fixed "out of range" error for big `--size`.
0.3.0 / 2019-10-12
------------------
- Added beta options `--lcd` & `--lcd-v` for subpixel rendering (still need
header info update).
- Added FreeType data properties to dump info.
- Fixed glyph width (missed fractional part after switch to FreeType).
- Fixed missed sigh for negative X/Y bitmap offsets.
- Deps bump.
0.2.0 / 2019-09-26
------------------
- Use FreeType renderer. Should solve all regressions, reported in 0.1.0.
- Enforced light autohinting (horizontal lines only).
- Use special hinter for monochrome output (improve quality).
- API changed to async.
- Fix: added missed `.bitmap_format` field to lvgl writer.
- Fix: changed struct fields init order to match declaration, #25.
0.1.0 / 2019-09-03
------------------
- First release.

22
node_modules/lv_font_conv/LICENSE generated vendored
View File

@ -1,22 +0,0 @@
Copyright (c) 2018 authors
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.

150
node_modules/lv_font_conv/README.md generated vendored
View File

@ -1,150 +0,0 @@
lv_font_conv - font convertor to compact bitmap format
======================================================
[![CI](https://github.com/lvgl/lv_font_conv/workflows/CI/badge.svg?branch=master)](https://github.com/lvgl/lv_font_conv/actions)
[![NPM version](https://img.shields.io/npm/v/lv_font_conv.svg?style=flat)](https://www.npmjs.org/package/lv_font_conv)
Converts TTF/WOFF/OTF fonts to __[compact format](https://github.com/lvgl/lv_font_conv/blob/master/doc/font_spec.md)__, suitable for small embedded systems. Main features are:
- Allows bitonal and anti-aliased glyphs (1-4 bits per pixel).
- Preserves kerning info.
- Compression.
- Users can select required glyphs only (subsetting).
- Multiple font sources can be merged.
- Simple CLI interface, easy to integrate into external build systems.
## Install the script
[node.js](https://nodejs.org/en/download/) v10+ required.
Global install of the last version, execute as "lv_font_conv"
```sh
# install release from npm registry
npm i lv_font_conv -g
# install from github's repo, master branch
npm i lvgl/lv_font_conv -g
```
**run via [npx](https://www.npmjs.com/package/npx) without install**
```sh
# run from npm registry
npx lv_font_conv -h
# run from github master
npx github:lvgl/lv_font_conv -h
```
Note, runing via `npx` may take some time until modules installed, be patient.
## CLI params
Common:
- `--bpp` - bits per pixel (antialiasing).
- `--size` - output font size (pixels).
- `-o`, `--output` - output path (file or directory, depends on format).
- `--format` - output format.
- `--format dump` - dump glyph images and font info, useful for debug.
- `--format bin` - dump font in binary form (as described in [spec](https://github.com/lvgl/lv_font_conv/blob/master/doc/font_spec.md)).
- `--format lvgl` - dump font in [LittlevGL](https://github.com/lvgl/lvgl) format.
- `--force-fast-kern-format` - always use more fast kering storage format,
at cost of some size. If size difference appears, it will be displayed.
- `--lcd` - generate bitmaps with 3x horizontal resolution, for subpixel
smoothing.
- `--lcd-v` - generate bitmaps with 3x vertical resolution, for subpixel
smoothing.
- `--use-color-info` - try to use glyph color info from font to create
grayscale icons. Since gray tones are emulated via transparency, result
will be good on contrast background only.
- `--lv-include` - only with `--format lvgl`, set alternate path for `lvgl.h`.
Per font:
- `--font` - path to font file (ttf/woff/woff2/otf). May be used multiple time for
merge.
- `-r`, `--range` - single glyph or range + optional mapping, belongs to
previously declared `--font`. Can be used multiple times. Examples:
- `-r 0x1F450` - single value, dec or hex format.
- `-r 0x1F450-0x1F470` - range.
- `-r '0x1F450=>0xF005'` - single glyph with mapping.
- `-r '0x1F450-0x1F470=>0xF005'` - range with mapping.
- `-r 0x1F450 -r 0x1F451-0x1F470` - 2 ranges.
- `-r 0x1F450,0x1F451-0x1F470` - the same as above, but defined with single `-r`.
- `--symbols` - list of characters to copy (instead of numeric format in `-r`).
- `--symbols 0123456789.,` - extract chars to display numbers.
- `--autohint-off` - do not force autohinting ("light" is on by default).
- `--autohint-strong` - use more strong autohinting (will break kerning).
Additional debug options:
- `--no-compress` - disable built-in RLE compression.
- `--no-prefilter` - disable bitmap lines filter (XOR), used to improve
compression ratio.
- `--no-kerning` - drop kerning info to reduce size (not recommended).
- `--full-info` - don't shorten 'font_info.json' (include pixels data).
## Examples
Merge english from Roboto Regular and icons from Font Awesome, and show debug
info:
`env DEBUG=* lv_font_conv --font Roboto-Regular.ttf -r 0x20-0x7F --font FontAwesome.ttf -r 0xFE00=>0x81 --size 16 --format bin --bpp 3 --no-compress -o output.font`
Merge english & russian from Roboto Regular, and show debug info:
`env DEBUG=* lv_font_conv --font Roboto-Regular.ttf -r 0x20-0x7F -r 0x401,0x410-0x44F,0x451 --size 16 --format bin --bpp 3 --no-compress -o output.font`
Dump all Roboto glyphs to inspect icons and font details:
`lv_font_conv --font Roboto-Regular.ttf -r 0x20-0x7F --size 16 --format dump --bpp 3 -o ./dump`
**Note**. Option `--no-compress` exists temporary, to avoid confusion until LVGL
adds compression support.
## Technical notes
### Supported output formats
1. **bin** - universal binary format, as described in https://github.com/lvgl/lv_font_conv/tree/master/doc.
2. **lvgl** - format for LittlevGL, C file. Has minor limitations and a bit
bigger size, because C does not allow to effectively define relative offsets
in data blocks.
3. **dump** - create folder with each glyph in separate image, and other font
data as `json`. Useful for debug.
### Merged font metrics
When multiple fonts merged into one, sources can have different metrics. Result
will follow principles below:
1. No scaling. Glyphs will have exactly the same size, as intended by font authors.
2. The same baseline.
3. `OS/2` metrics (`sTypoAscender`, `sTypoDescender`, `sTypoLineGap`) will be
used from the first font in list.
4. `hhea` metrics (`ascender`, `descender`), defined as max/min point of all
font glyphs, are recalculated, according to new glyphs set.
## Development
Current package includes WebAssembly build of FreeType with some helper
functions. Everything is wrapped into Docker and requires zero knowledge about
additional tools install. See `package.json` for additional commands. You may
need those if decide to upgrade FreeType or update helpers.
This builds image with emscripten & freetype, usually should be done only once:
```
npm run build:dockerimage
```
This compiles helpers and creates WebAssembly files:
```
npm run build:freetype
```

View File

@ -1,9 +0,0 @@
// Custom Error type to simplify error messaging
//
'use strict';
//const ExtendableError = require('es6-error');
//module.exports = class AppError extends ExtendableError {};
module.exports = require('make-error')('AppError');

318
node_modules/lv_font_conv/lib/cli.js generated vendored
View File

@ -1,318 +0,0 @@
// Parse input arguments and execute convertor
'use strict';
const argparse = require('argparse');
const fs = require('fs');
const mkdirp = require('mkdirp');
const path = require('path');
const convert = require('./convert');
class ActionFontAdd extends argparse.Action {
call(parser, namespace, value/*, option_string*/) {
let items = (namespace[this.dest] || []).slice();
items.push({ source_path: value, ranges: [] });
namespace[this.dest] = items;
}
}
// add range or symbols to font;
// need to merge them into one array here so overrides work correctly
class ActionFontRangeAdd extends argparse.Action {
call(parser, namespace, value, option_string) {
let fonts = namespace.font || [];
if (fonts.length === 0) {
parser.error(`argument ${option_string}: Only allowed after --font`);
}
let lastFont = fonts[fonts.length - 1];
// { symbols: 'ABC' }, or { range: [ 65, 67, 65 ] }
lastFont.ranges.push({ [this.dest]: value });
}
}
// add hinting option to font;
class ActionFontStoreTrue extends argparse.Action {
constructor(options) {
options = options || {};
options.const = true;
options.default = options.default !== null ? options.default : false;
options.nargs = 0;
super(options);
}
call(parser, namespace, value, option_string) {
let fonts = namespace.font || [];
if (fonts.length === 0) {
parser.error(`argument ${option_string}: Only allowed after --font`);
}
let lastFont = fonts[fonts.length - 1];
lastFont[this.dest] = this.const;
}
}
// Formatter with support of `\n` in Help texts.
class RawTextHelpFormatter2 extends argparse.RawDescriptionHelpFormatter {
// executes parent _split_lines for each line of the help, then flattens the result
_split_lines(text, width) {
return [].concat(...text.split('\n').map(line => super._split_lines(line, width)));
}
}
// parse decimal or hex code in unicode range
function unicode_point(str) {
let m = /^(?:(?:0x([0-9a-f]+))|([0-9]+))$/i.exec(str.trim());
if (!m) throw new TypeError(`${str} is not a number`);
let [ , hex, dec ] = m;
let value = hex ? parseInt(hex, 16) : parseInt(dec, 10);
if (value > 0x10FFFF) throw new TypeError(`${str} is out of unicode range`);
return value;
}
// parse range
function range(str) {
let result = [];
for (let s of str.split(',')) {
let m = /^(.+?)(?:-(.+?))?(?:=>(.+?))?$/i.exec(s);
let [ , start, end, mapped_start ] = m;
if (!end) end = start;
if (!mapped_start) mapped_start = start;
start = unicode_point(start);
end = unicode_point(end);
if (start > end) throw new TypeError(`Invalid range: ${s}`);
mapped_start = unicode_point(mapped_start);
result.push(start, end, mapped_start);
}
return result;
}
// exclude negative numbers and non-numbers
function positive_int(str) {
if (!/^\d+$/.test(str)) throw new TypeError(`${str} is not a valid number`);
let n = parseInt(str, 10);
if (n <= 0) throw new TypeError(`${str} is not a valid number`);
return n;
}
module.exports.run = async function (argv, debug = false) {
//
// Configure CLI
//
let parser = new argparse.ArgumentParser({
add_help: true,
formatter_class: RawTextHelpFormatter2
});
if (debug) {
parser.exit = function (status, message) {
throw new Error(message);
};
}
parser.add_argument('-v', '--version', {
action: 'version',
version: require('../package.json').version
});
parser.add_argument('--size', {
metavar: 'PIXELS',
type: positive_int,
required: true,
help: 'Output font size, pixels.'
});
parser.add_argument('-o', '--output', {
metavar: '<path>',
help: 'Output path.'
});
parser.add_argument('--bpp', {
choices: [ 1, 2, 3, 4, 8 ],
type: positive_int,
required: true,
help: 'Bits per pixel, for antialiasing.'
});
let lcd_group = parser.add_mutually_exclusive_group();
lcd_group.add_argument('--lcd', {
action: 'store_true',
default: false,
help: 'Enable subpixel rendering (horizontal pixel layout).'
});
lcd_group.add_argument('--lcd-v', {
action: 'store_true',
default: false,
help: 'Enable subpixel rendering (vertical pixel layout).'
});
parser.add_argument('--use-color-info', {
dest: 'use_color_info',
action: 'store_true',
default: false,
help: 'Try to use glyph color info from font to create grayscale icons. ' +
'Since gray tones are emulated via transparency, result will be good on contrast background only.'
});
parser.add_argument('--format', {
choices: convert.formats,
required: true,
help: 'Output format.'
});
parser.add_argument('--font', {
metavar: '<path>',
action: ActionFontAdd,
required: true,
help: 'Source font path. Can be used multiple times to merge glyphs from different fonts.'
});
parser.add_argument('-r', '--range', {
type: range,
action: ActionFontRangeAdd,
help: `
Range of glyphs to copy. Can be used multiple times, belongs to previously declared "--font". Examples:
-r 0x1F450
-r 0x20-0x7F
-r 32-127
-r 32-127,0x1F450
-r '0x1F450=>0xF005'
-r '0x1F450-0x1F470=>0xF005'
`
});
parser.add_argument('--symbols', {
action: ActionFontRangeAdd,
help: `
List of characters to copy, belongs to previously declared "--font". Examples:
--symbols ,.0123456789
--symbols abcdefghigklmnopqrstuvwxyz
`
});
parser.add_argument('--autohint-off', {
type: range,
action: ActionFontStoreTrue,
help: 'Disable autohinting for previously declared "--font"'
});
parser.add_argument('--autohint-strong', {
type: range,
action: ActionFontStoreTrue,
help: 'Use more strong autohinting for previously declared "--font" (will break kerning)'
});
parser.add_argument('--force-fast-kern-format', {
dest: 'fast_kerning',
action: 'store_true',
default: false,
help: 'Always use kern classes instead of pairs (might be larger but faster).'
});
parser.add_argument('--no-compress', {
dest: 'no_compress',
action: 'store_true',
default: false,
help: 'Disable built-in RLE compression.'
});
parser.add_argument('--no-prefilter', {
dest: 'no_prefilter',
action: 'store_true',
default: false,
help: 'Disable bitmap lines filter (XOR), used to improve compression ratio.'
});
parser.add_argument('--no-kerning', {
dest: 'no_kerning',
action: 'store_true',
default: false,
help: 'Drop kerning info to reduce size (not recommended).'
});
parser.add_argument('--lv-include', {
metavar: '<path>',
help: 'Set alternate "lvgl.h" path (for --format lvgl).'
});
parser.add_argument('--full-info', {
dest: 'full_info',
action: 'store_true',
default: false,
help: 'Don\'t shorten "font_info.json" (include pixels data).'
});
//
// Process CLI options
//
let args = parser.parse_args(argv.length ? argv : [ '-h' ]);
for (let font of args.font) {
if (font.ranges.length === 0) {
parser.error(`You need to specify either "--range" or "--symbols" for font "${font.source_path}"`);
}
try {
font.source_bin = fs.readFileSync(font.source_path);
} catch (err) {
parser.error(`Cannot read file "${font.source_path}": ${err.message}`);
}
}
//
// Convert
//
let files = await convert(args);
//
// Store files
//
for (let [ filename, data ] of Object.entries(files)) {
let dir = path.dirname(filename);
mkdirp.sync(dir);
fs.writeFileSync(filename, data);
}
};
// export for tests
module.exports._range = range;

View File

@ -1,173 +0,0 @@
// Read fonts
'use strict';
const opentype = require('opentype.js');
const ft_render = require('./freetype');
const AppError = require('./app_error');
const Ranger = require('./ranger');
module.exports = async function collect_font_data(args) {
await ft_render.init();
// Duplicate font options as k/v for quick access
let fonts_options = {};
args.font.forEach(f => { fonts_options[f.source_path] = f; });
// read fonts
let fonts_opentype = {};
let fonts_freetype = {};
for (let { source_path, source_bin } of args.font) {
// don't load font again if it's specified multiple times in args
if (fonts_opentype[source_path]) continue;
try {
let b = source_bin;
if (Buffer.isBuffer(b)) {
// node.js Buffer -> ArrayBuffer
b = b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
}
fonts_opentype[source_path] = opentype.parse(b);
} catch (err) {
throw new AppError(`Cannot load font "${source_path}": ${err.message}`);
}
fonts_freetype[source_path] = ft_render.fontface_create(source_bin, args.size);
}
// merge all ranges
let ranger = new Ranger();
for (let { source_path, ranges } of args.font) {
let font = fonts_freetype[source_path];
for (let item of ranges) {
/* eslint-disable max-depth */
if (item.range) {
for (let i = 0; i < item.range.length; i += 3) {
let range = item.range.slice(i, i + 3);
let chars = ranger.add_range(source_path, ...range);
let is_empty = true;
for (let code of chars) {
if (ft_render.glyph_exists(font, code)) {
is_empty = false;
break;
}
}
if (is_empty) {
let a = '0x' + range[0].toString(16);
let b = '0x' + range[1].toString(16);
throw new AppError(`Font "${source_path}" doesn't have any characters included in range ${a}-${b}`);
}
}
}
if (item.symbols) {
let chars = ranger.add_symbols(source_path, item.symbols);
let is_empty = true;
for (let code of chars) {
if (ft_render.glyph_exists(font, code)) {
is_empty = false;
break;
}
}
if (is_empty) {
throw new AppError(`Font "${source_path}" doesn't have any characters included in "${item.symbols}"`);
}
}
}
}
let mapping = ranger.get();
let glyphs = [];
let all_dst_charcodes = Object.keys(mapping).sort((a, b) => a - b).map(Number);
for (let dst_code of all_dst_charcodes) {
let src_code = mapping[dst_code].code;
let src_font = mapping[dst_code].font;
if (!ft_render.glyph_exists(fonts_freetype[src_font], src_code)) continue;
let ft_result = ft_render.glyph_render(
fonts_freetype[src_font],
src_code,
{
autohint_off: fonts_options[src_font].autohint_off,
autohint_strong: fonts_options[src_font].autohint_strong,
lcd: args.lcd,
lcd_v: args.lcd_v,
mono: !args.lcd && !args.lcd_v && args.bpp === 1,
use_color_info: args.use_color_info
}
);
glyphs.push({
code: dst_code,
advanceWidth: ft_result.advance_x,
bbox: {
x: ft_result.x,
y: ft_result.y - ft_result.height,
width: ft_result.width,
height: ft_result.height
},
kerning: {},
freetype: ft_result.freetype,
pixels: ft_result.pixels
});
}
if (!args.no_kerning) {
let existing_dst_charcodes = glyphs.map(g => g.code);
for (let { code, kerning } of glyphs) {
let src_code = mapping[code].code;
let src_font = mapping[code].font;
let font = fonts_opentype[src_font];
let glyph = font.charToGlyph(String.fromCodePoint(src_code));
for (let dst_code2 of existing_dst_charcodes) {
// can't merge kerning values from 2 different fonts
if (mapping[dst_code2].font !== src_font) continue;
let src_code2 = mapping[dst_code2].code;
let glyph2 = font.charToGlyph(String.fromCodePoint(src_code2));
let krn_value = font.getKerningValue(glyph, glyph2);
if (krn_value) kerning[dst_code2] = krn_value * args.size / font.unitsPerEm;
//let krn_value = ft_render.get_kerning(font, src_code, src_code2).x;
//if (krn_value) kerning[dst_code2] = krn_value;
}
}
}
let first_font = fonts_freetype[args.font[0].source_path];
let first_font_scale = args.size / first_font.units_per_em;
let os2_metrics = ft_render.fontface_os2_table(first_font);
let post_table = fonts_opentype[args.font[0].source_path].tables.post;
for (let font of Object.values(fonts_freetype)) ft_render.fontface_destroy(font);
ft_render.destroy();
return {
ascent: Math.max(...glyphs.map(g => g.bbox.y + g.bbox.height)),
descent: Math.min(...glyphs.map(g => g.bbox.y)),
typoAscent: Math.round(os2_metrics.typoAscent * first_font_scale),
typoDescent: Math.round(os2_metrics.typoDescent * first_font_scale),
typoLineGap: Math.round(os2_metrics.typoLineGap * first_font_scale),
size: args.size,
glyphs,
underlinePosition: Math.round(post_table.underlinePosition * first_font_scale),
underlineThickness: Math.round(post_table.underlineThickness * first_font_scale)
};
};

View File

@ -1,30 +0,0 @@
// Internal API to convert input data into output font data
// Used by both CLI and Web wrappers.
'use strict';
const collect_font_data = require('./collect_font_data');
let writers = {
dump: require('./writers/dump'),
bin: require('./writers/bin'),
lvgl: require('./writers/lvgl')
};
//
// Input:
// - args like from CLI (optionally extended with binary content of files)
//
// Output:
// - { name1: bin_data1, name2: bin_data2, ... }
//
// returns hash with files to write
//
module.exports = async function convert(args) {
let font_data = await collect_font_data(args);
let files = writers[args.format](args, font_data);
return files;
};
module.exports.formats = Object.keys(writers);

View File

@ -1,105 +0,0 @@
// Find an optimal configuration of cmap tables representing set of codepoints,
// using simple breadth-first algorithm
//
// Assume that:
// - codepoints have one-to-one correspondence to glyph ids
// - glyph ids are always bigger for bigger codepoints
// - glyph ids are always consecutive (1..N without gaps)
//
// This way we can omit glyph ids from all calculations entirely: if codepoints
// fit in format0, then glyph ids also will.
//
// format6 is not considered, because if glyph ids can be delta-coded,
// multiple format0 tables are guaranteed to be smaller than a single format6.
//
// sparse format is not used because as long as glyph ids are consecutive,
// sparse_tiny will always be preferred.
//
'use strict';
function estimate_format0_tiny_size(/*start_code, end_code*/) {
return 16;
}
function estimate_format0_size(start_code, end_code) {
return 16 + (end_code - start_code + 1);
}
//function estimate_sparse_size(count) {
// return 16 + count * 4;
//}
function estimate_sparse_tiny_size(count) {
return 16 + count * 2;
}
module.exports = function cmap_split(all_codepoints) {
all_codepoints = all_codepoints.sort((a, b) => a - b);
let min_paths = [];
for (let i = 0; i < all_codepoints.length; i++) {
let min = { dist: Infinity };
for (let j = 0; j <= i; j++) {
let prev_dist = (j - 1 >= 0) ? min_paths[j - 1].dist : 0;
let s;
if (all_codepoints[i] - all_codepoints[j] < 256) {
s = estimate_format0_size(all_codepoints[j], all_codepoints[i]);
/* eslint-disable max-depth */
if (prev_dist + s < min.dist) {
min = {
dist: prev_dist + s,
start: j,
end: i,
format: 'format0'
};
}
}
if (all_codepoints[i] - all_codepoints[j] < 256 && all_codepoints[i] - i === all_codepoints[j] - j) {
s = estimate_format0_tiny_size(all_codepoints[j], all_codepoints[i]);
/* eslint-disable max-depth */
if (prev_dist + s < min.dist) {
min = {
dist: prev_dist + s,
start: j,
end: i,
format: 'format0_tiny'
};
}
}
// tiny sparse will always be preferred over full sparse because glyph ids are consecutive
if (all_codepoints[i] - all_codepoints[j] < 65536) {
s = estimate_sparse_tiny_size(i - j + 1);
if (prev_dist + s < min.dist) {
min = {
dist: prev_dist + s,
start: j,
end: i,
format: 'sparse_tiny'
};
}
}
}
min_paths[i] = min;
}
let result = [];
for (let i = all_codepoints.length - 1; i >= 0;) {
let path = min_paths[i];
result.unshift([ path.format, all_codepoints.slice(path.start, path.end + 1) ]);
i = path.start - 1;
}
return result;
};

View File

@ -1,107 +0,0 @@
'use strict';
//const debug = require('debug')('compress');
function count_same(arr, offset) {
let same = 1;
let val = arr[offset];
for (let i = offset + 1; i < arr.length; i++) {
if (arr[i] !== val) break;
same++;
}
return same;
}
//
// Compress pixels with RLE-like algorythm (modified I3BN)
//
// 1. Require minimal repeat count (1) to enter I3BN mode
// 2. Increased 1-bit-replaced repeat limit (2 => 10)
// 3. Length of direct repetition counter reduced (8 => 6 bits).
//
// pixels - flat array of pixels (one per entry)
// options.bpp - bits per pixels
//
module.exports = function compress(bitStream, pixels, options) {
const opts = Object.assign({}, { repeat: 1 }, options);
// Minimal repetitions count to enable RLE mode.
const RLE_SKIP_COUNT = 1;
// Number of repeats, when `1` used to replace data
// If more - write as number
const RLE_BIT_COLLAPSED_COUNT = 10;
const RLE_COUNTER_BITS = 6; // (2^bits - 1) - max value
const RLE_COUNTER_MAX = (1 << RLE_COUNTER_BITS) - 1;
// Force flush if counter dencity exceeded.
const RLE_MAX_REPEATS = RLE_COUNTER_MAX + RLE_BIT_COLLAPSED_COUNT + 1;
//let bits_start_offset = bitStream.index;
let offset = 0;
while (offset < pixels.length) {
const p = pixels[offset];
let same = count_same(pixels, offset);
// Clamp value because RLE counter density is limited
if (same > RLE_MAX_REPEATS + RLE_SKIP_COUNT) {
same = RLE_MAX_REPEATS + RLE_SKIP_COUNT;
}
//debug(`offset: ${offset}, count: ${same}, pixel: ${p}`);
offset += same;
// If not enough for RLE - write as is.
if (same <= RLE_SKIP_COUNT) {
for (let i = 0; i < same; i++) {
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits`);
}
continue;
}
// First, write "skipped" head as is.
for (let i = 0; i < RLE_SKIP_COUNT; i++) {
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits`);
}
same -= RLE_SKIP_COUNT;
// Not reached state to use counter => dump bit-extended
if (same <= RLE_BIT_COLLAPSED_COUNT) {
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits (val)`);
for (let i = 0; i < same; i++) {
/*eslint-disable max-depth*/
if (i < same - 1) {
bitStream.writeBits(1, 1);
//debug('==> 1 bit (rle repeat)');
} else {
bitStream.writeBits(0, 1);
//debug('==> 1 bit (rle repeat last)');
}
}
continue;
}
same -= RLE_BIT_COLLAPSED_COUNT + 1;
bitStream.writeBits(p, opts.bpp);
//debug(`==> ${opts.bpp} bits (val)`);
for (let i = 0; i < RLE_BIT_COLLAPSED_COUNT + 1; i++) {
bitStream.writeBits(1, 1);
//debug('==> 1 bit (rle repeat)');
}
bitStream.writeBits(same, RLE_COUNTER_BITS);
//debug(`==> 4 bits (rle repeat count ${same})`);
}
//debug(`output bits: ${bitStream.index - bits_start_offset}`);
};

View File

@ -1,131 +0,0 @@
// Font class to generate tables
'use strict';
const u = require('../utils');
const debug = require('debug')('font');
const Head = require('./table_head');
const Cmap = require('./table_cmap');
const Glyf = require('./table_glyf');
const Loca = require('./table_loca');
const Kern = require('./table_kern');
class Font {
constructor(fontData, options) {
this.src = fontData;
this.opts = options;
// Map chars to IDs (zero is reserved)
this.glyph_id = { 0: 0 };
this.last_id = 1;
this.createIDs();
debug(`last_id: ${this.last_id}`);
this.init_tables();
this.minY = Math.min(...this.src.glyphs.map(g => g.bbox.y));
debug(`minY: ${this.minY}`);
this.maxY = Math.max(...this.src.glyphs.map(g => g.bbox.y + g.bbox.height));
debug(`maxY: ${this.maxY}`);
// 0 => 1 byte, 1 => 2 bytes
this.glyphIdFormat = Math.max(...Object.values(this.glyph_id)) > 255 ? 1 : 0;
debug(`glyphIdFormat: ${this.glyphIdFormat}`);
// 1.0 by default, will be stored in font as FP12.4
this.kerningScale = 1.0;
let kerningMax = Math.max(...this.src.glyphs.map(g => Object.values(g.kerning).map(Math.abs)).flat());
if (kerningMax >= 7.5) this.kerningScale = Math.ceil(kerningMax / 7.5 * 16) / 16;
debug(`kerningScale: ${this.kerningScale}`);
// 0 => int, 1 => FP4
this.advanceWidthFormat = this.hasKerning() ? 1 : 0;
debug(`advanceWidthFormat: ${this.advanceWidthFormat}`);
this.xy_bits = Math.max(...this.src.glyphs.map(g => Math.max(
u.signed_bits(g.bbox.x), u.signed_bits(g.bbox.y)
)));
debug(`xy_bits: ${this.xy_bits}`);
this.wh_bits = Math.max(...this.src.glyphs.map(g => Math.max(
u.unsigned_bits(g.bbox.width), u.unsigned_bits(g.bbox.height)
)));
debug(`wh_bits: ${this.wh_bits}`);
this.advanceWidthBits = Math.max(...this.src.glyphs.map(
g => u.signed_bits(this.widthToInt(g.advanceWidth))
));
debug(`advanceWidthBits: ${this.advanceWidthBits}`);
let glyphs = this.src.glyphs;
this.monospaced = glyphs.every((v, i, arr) => v.advanceWidth === arr[0].advanceWidth);
debug(`monospaced: ${this.monospaced}`);
// This should stay in the end, because depends on previous variables
// 0 => 2 bytes, 1 => 4 bytes
this.indexToLocFormat = this.glyf.getSize() > 65535 ? 1 : 0;
debug(`indexToLocFormat: ${this.indexToLocFormat}`);
this.subpixels_mode = options.lcd ? 1 : (options.lcd_v ? 2 : 0);
debug(`subpixels_mode: ${this.subpixels_mode}`);
}
init_tables() {
this.head = new Head(this);
this.glyf = new Glyf(this);
this.cmap = new Cmap(this);
this.loca = new Loca(this);
this.kern = new Kern(this);
}
createIDs() {
// Simplified, don't check dupes
this.last_id = 1;
for (let i = 0; i < this.src.glyphs.length; i++) {
// reserve zero for special cases
this.glyph_id[this.src.glyphs[i].code] = this.last_id;
this.last_id++;
}
}
hasKerning() {
if (this.opts.no_kerning) return false;
for (let glyph of this.src.glyphs) {
if (glyph.kerning && Object.keys(glyph.kerning).length) return true;
}
return false;
}
// Returns integer width, depending on format
widthToInt(val) {
if (this.advanceWidthFormat === 0) return Math.round(val);
return Math.round(val * 16);
}
// Convert kerning to FP4.4, useable for writer. Apply `kerningScale`.
kernToFP(val) {
return Math.round(val / this.kerningScale * 16);
}
toBin() {
const result = Buffer.concat([
this.head.toBin(),
this.cmap.toBin(),
this.loca.toBin(),
this.glyf.toBin(),
this.kern.toBin()
]);
debug(`font size: ${result.length}`);
return result;
}
}
module.exports = Font;

View File

@ -1,201 +0,0 @@
'use strict';
const build_subtables = require('./cmap_build_subtables');
const u = require('../utils');
const debug = require('debug')('font.table.cmap');
const O_SIZE = 0;
const O_LABEL = O_SIZE + 4;
const O_COUNT = O_LABEL + 4;
const HEAD_LENGTH = O_COUNT + 4;
const SUB_FORMAT_0 = 0;
const SUB_FORMAT_0_TINY = 2;
const SUB_FORMAT_SPARSE = 1;
const SUB_FORMAT_SPARSE_TINY = 3;
class Cmap {
constructor(font) {
this.font = font;
this.label = 'cmap';
this.sub_heads = [];
this.sub_data = [];
this.compiled = false;
}
compile() {
if (this.compiled) return;
this.compiled = true;
const f = this.font;
let subtables_plan = build_subtables(f.src.glyphs.map(g => g.code));
const count_format0 = subtables_plan.filter(s => s[0] === 'format0').length;
const count_sparse = subtables_plan.length - count_format0;
debug(`${subtables_plan.length} subtable(s): ${count_format0} "format 0", ${count_sparse} "sparse"`);
for (let [ format, codepoints ] of subtables_plan) {
let g = this.glyphByCode(codepoints[0]);
let start_glyph_id = f.glyph_id[g.code];
let min_code = codepoints[0];
let max_code = codepoints[codepoints.length - 1];
let entries_count = max_code - min_code + 1;
let format_code = 0;
if (format === 'format0_tiny') {
format_code = SUB_FORMAT_0_TINY;
this.sub_data.push(Buffer.alloc(0));
} else if (format === 'format0') {
format_code = SUB_FORMAT_0;
this.sub_data.push(this.create_format0_data(min_code, max_code, start_glyph_id));
} else if (format === 'sparse_tiny') {
entries_count = codepoints.length;
format_code = SUB_FORMAT_SPARSE_TINY;
this.sub_data.push(this.create_sparse_tiny_data(codepoints, start_glyph_id));
} else { // assume format === 'sparse'
entries_count = codepoints.length;
format_code = SUB_FORMAT_SPARSE;
this.sub_data.push(this.create_sparse_data(codepoints, start_glyph_id));
}
this.sub_heads.push(this.createSubHeader(
min_code,
max_code - min_code + 1,
start_glyph_id,
entries_count,
format_code
));
}
this.subHeaderUpdateAllOffsets();
}
createSubHeader(rangeStart, rangeLen, glyphIdOffset, total, type) {
const buf = Buffer.alloc(16);
// buf.writeUInt32LE(offset, 0); offset unknown at this moment
buf.writeUInt32LE(rangeStart, 4);
buf.writeUInt16LE(rangeLen, 8);
buf.writeUInt16LE(glyphIdOffset, 10);
buf.writeUInt16LE(total, 12);
buf.writeUInt8(type, 14);
return buf;
}
subHeaderUpdateOffset(header, val) {
header.writeUInt32LE(val, 0);
}
subHeaderUpdateAllOffsets() {
for (let i = 0; i < this.sub_heads.length; i++) {
const offset = HEAD_LENGTH +
u.sum(this.sub_heads.map(h => h.length)) +
u.sum(this.sub_data.slice(0, i).map(d => d.length));
this.subHeaderUpdateOffset(this.sub_heads[i], offset);
}
}
glyphByCode(code) {
for (let g of this.font.src.glyphs) {
if (g.code === code) return g;
}
return null;
}
collect_format0_data(min_code, max_code, start_glyph_id) {
let data = [];
for (let i = min_code; i <= max_code; i++) {
const g = this.glyphByCode(i);
if (!g) {
data.push(0);
continue;
}
const id_delta = this.font.glyph_id[g.code] - start_glyph_id;
if (id_delta < 0 || id_delta > 255) throw new Error('Glyph ID delta out of Format 0 range');
data.push(id_delta);
}
return data;
}
create_format0_data(min_code, max_code, start_glyph_id) {
const data = this.collect_format0_data(min_code, max_code, start_glyph_id);
return u.balign4(Buffer.from(data));
}
collect_sparse_data(codepoints, start_glyph_id) {
let codepoints_list = [];
let ids_list = [];
for (let code of codepoints) {
let g = this.glyphByCode(code);
let id = this.font.glyph_id[g.code];
let code_delta = code - codepoints[0];
let id_delta = id - start_glyph_id;
if (code_delta < 0 || code_delta > 65535) throw new Error('Codepoint delta out of range');
if (id_delta < 0 || id_delta > 65535) throw new Error('Glyph ID delta out of range');
codepoints_list.push(code_delta);
ids_list.push(id_delta);
}
return {
codes: codepoints_list,
ids: ids_list
};
}
create_sparse_data(codepoints, start_glyph_id) {
const data = this.collect_sparse_data(codepoints, start_glyph_id);
return u.balign4(Buffer.concat([
u.bFromA16(data.codes),
u.bFromA16(data.ids)
]));
}
create_sparse_tiny_data(codepoints, start_glyph_id) {
const data = this.collect_sparse_data(codepoints, start_glyph_id);
return u.balign4(u.bFromA16(data.codes));
}
toBin() {
if (!this.compiled) this.compile();
const buf = Buffer.concat([
Buffer.alloc(HEAD_LENGTH),
Buffer.concat(this.sub_heads),
Buffer.concat(this.sub_data)
]);
debug(`table size = ${buf.length}`);
buf.writeUInt32LE(buf.length, O_SIZE);
buf.write(this.label, O_LABEL);
buf.writeUInt32LE(this.sub_heads.length, O_COUNT);
return buf;
}
}
module.exports = Cmap;

View File

@ -1,147 +0,0 @@
'use strict';
const u = require('../utils');
const { BitStream } = require('bit-buffer');
const debug = require('debug')('font.table.glyf');
const compress = require('./compress');
const O_SIZE = 0;
const O_LABEL = O_SIZE + 4;
const HEAD_LENGTH = O_LABEL + 4;
class Glyf {
constructor(font) {
this.font = font;
this.label = 'glyf';
this.compiled = false;
this.binData = [];
}
// convert 8-bit opacity to bpp-bit
pixelsToBpp(pixels) {
const bpp = this.font.opts.bpp;
return pixels.map(line => line.map(p => (p >>> (8 - bpp))));
}
// Returns "binary stream" (Buffer) of compiled glyph data
compileGlyph(glyph) {
// Allocate memory, enough for eny storage formats
const buf = Buffer.alloc(100 + glyph.bbox.width * glyph.bbox.height * 4);
const bs = new BitStream(buf);
bs.bigEndian = true;
const f = this.font;
// Store Width
if (!f.monospaced) {
let w = f.widthToInt(glyph.advanceWidth);
bs.writeBits(w, f.advanceWidthBits);
}
// Store X, Y
bs.writeBits(glyph.bbox.x, f.xy_bits);
bs.writeBits(glyph.bbox.y, f.xy_bits);
bs.writeBits(glyph.bbox.width, f.wh_bits);
bs.writeBits(glyph.bbox.height, f.wh_bits);
const pixels = this.pixelsToBpp(glyph.pixels);
this.storePixels(bs, pixels);
// Shrink size
const result = Buffer.alloc(bs.byteIndex);
buf.copy(result, 0, 0, bs.byteIndex);
return result;
}
storePixels(bitStream, pixels) {
if (this.getCompressionCode() === 0) this.storePixelsRaw(bitStream, pixels);
else this.storePixelsCompressed(bitStream, pixels);
}
storePixelsRaw(bitStream, pixels) {
const bpp = this.font.opts.bpp;
for (let y = 0; y < pixels.length; y++) {
const line = pixels[y];
for (let x = 0; x < line.length; x++) {
bitStream.writeBits(line[x], bpp);
}
}
}
storePixelsCompressed(bitStream, pixels) {
let p;
if (this.font.opts.no_prefilter) p = pixels.flat();
else p = u.prefilter(pixels).flat();
compress(bitStream, p, this.font.opts);
}
// Create internal struct with binary data for each glyph
// Needed to calculate offsets & build final result
compile() {
this.compiled = true;
this.binData = [
Buffer.alloc(0) // Reserve id 0
];
const f = this.font;
f.src.glyphs.forEach(g => {
const id = f.glyph_id[g.code];
this.binData[id] = this.compileGlyph(g);
});
}
toBin() {
if (!this.compiled) this.compile();
const buf = u.balign4(Buffer.concat([
Buffer.alloc(HEAD_LENGTH),
Buffer.concat(this.binData)
]));
buf.writeUInt32LE(buf.length, O_SIZE);
buf.write(this.label, O_LABEL);
debug(`table size = ${buf.length}`);
return buf;
}
getSize() {
if (!this.compiled) this.compile();
return u.align4(HEAD_LENGTH + u.sum(this.binData.map(b => b.length)));
}
getOffset(id) {
if (!this.compiled) this.compile();
let offset = HEAD_LENGTH;
for (let i = 0; i < id; i++) offset += this.binData[i].length;
return offset;
}
getCompressionCode() {
if (this.font.opts.no_compress) return 0;
if (this.font.opts.bpp === 1) return 0;
if (this.font.opts.no_prefilter) return 2;
return 1;
}
}
module.exports = Glyf;

View File

@ -1,99 +0,0 @@
'use strict';
const u = require('../utils');
const debug = require('debug')('font.table.head');
const O_SIZE = 0;
const O_LABEL = O_SIZE + 4;
const O_VERSION = O_LABEL + 4;
const O_TABLES = O_VERSION + 4;
const O_FONT_SIZE = O_TABLES + 2;
const O_ASCENT = O_FONT_SIZE + 2;
const O_DESCENT = O_ASCENT + 2;
const O_TYPO_ASCENT = O_DESCENT + 2;
const O_TYPO_DESCENT = O_TYPO_ASCENT + 2;
const O_TYPO_LINE_GAP = O_TYPO_DESCENT + 2;
const O_MIN_Y = O_TYPO_LINE_GAP + 2;
const O_MAX_Y = O_MIN_Y + 2;
const O_DEF_ADVANCE_WIDTH = O_MAX_Y + 2;
const O_KERNING_SCALE = O_DEF_ADVANCE_WIDTH + 2;
const O_INDEX_TO_LOC_FORMAT = O_KERNING_SCALE + 2;
const O_GLYPH_ID_FORMAT = O_INDEX_TO_LOC_FORMAT + 1;
const O_ADVANCE_WIDTH_FORMAT = O_GLYPH_ID_FORMAT + 1;
const O_BITS_PER_PIXEL = O_ADVANCE_WIDTH_FORMAT + 1;
const O_XY_BITS = O_BITS_PER_PIXEL + 1;
const O_WH_BITS = O_XY_BITS + 1;
const O_ADVANCE_WIDTH_BITS = O_WH_BITS + 1;
const O_COMPRESSION_ID = O_ADVANCE_WIDTH_BITS + 1;
const O_SUBPIXELS_MODE = O_COMPRESSION_ID + 1;
const O_TMP_RESERVED1 = O_SUBPIXELS_MODE + 1;
const O_UNDERLINE_POSITION = O_TMP_RESERVED1 + 1;
const O_UNDERLINE_THICKNESS = O_UNDERLINE_POSITION + 2;
const HEAD_LENGTH = u.align4(O_UNDERLINE_THICKNESS + 2);
class Head {
constructor(font) {
this.font = font;
this.label = 'head';
this.version = 1;
}
toBin() {
const buf = Buffer.alloc(HEAD_LENGTH);
debug(`table size = ${buf.length}`);
buf.writeUInt32LE(HEAD_LENGTH, O_SIZE);
buf.write(this.label, O_LABEL);
buf.writeUInt32LE(this.version, O_VERSION);
const f = this.font;
const tables_count = f.hasKerning() ? 4 : 3;
buf.writeUInt16LE(tables_count, O_TABLES);
buf.writeUInt16LE(f.src.size, O_FONT_SIZE);
buf.writeUInt16LE(f.src.ascent, O_ASCENT);
buf.writeInt16LE(f.src.descent, O_DESCENT);
buf.writeUInt16LE(f.src.typoAscent, O_TYPO_ASCENT);
buf.writeInt16LE(f.src.typoDescent, O_TYPO_DESCENT);
buf.writeUInt16LE(f.src.typoLineGap, O_TYPO_LINE_GAP);
buf.writeInt16LE(f.minY, O_MIN_Y);
buf.writeInt16LE(f.maxY, O_MAX_Y);
if (f.monospaced) {
buf.writeUInt16LE(f.widthToInt(f.src.glyphs[0].advanceWidth), O_DEF_ADVANCE_WIDTH);
} else {
buf.writeUInt16LE(0, O_DEF_ADVANCE_WIDTH);
}
buf.writeUInt16LE(Math.round(f.kerningScale * 16), O_KERNING_SCALE); // FP12.4
buf.writeUInt8(f.indexToLocFormat, O_INDEX_TO_LOC_FORMAT);
buf.writeUInt8(f.glyphIdFormat, O_GLYPH_ID_FORMAT);
buf.writeUInt8(f.advanceWidthFormat, O_ADVANCE_WIDTH_FORMAT);
buf.writeUInt8(f.opts.bpp, O_BITS_PER_PIXEL);
buf.writeUInt8(f.xy_bits, O_XY_BITS);
buf.writeUInt8(f.wh_bits, O_WH_BITS);
if (f.monospaced) buf.writeUInt8(0, O_ADVANCE_WIDTH_BITS);
else buf.writeUInt8(f.advanceWidthBits, O_ADVANCE_WIDTH_BITS);
buf.writeUInt8(f.glyf.getCompressionCode(), O_COMPRESSION_ID);
buf.writeUInt8(f.subpixels_mode, O_SUBPIXELS_MODE);
buf.writeInt16LE(f.src.underlinePosition, O_UNDERLINE_POSITION);
buf.writeUInt16LE(f.src.underlineThickness, O_UNDERLINE_POSITION);
return buf;
}
}
module.exports = Head;

View File

@ -1,256 +0,0 @@
'use strict';
const u = require('../utils');
const debug = require('debug')('font.table.kern');
const O_SIZE = 0;
const O_LABEL = O_SIZE + 4;
const O_FORMAT = O_LABEL + 4;
const HEAD_LENGTH = u.align4(O_FORMAT + 1);
class Kern {
constructor(font) {
this.font = font;
this.label = 'kern';
this.format3_forced = false;
}
collect_format0_data() {
const f = this.font;
const glyphs = u.sort_by(this.font.src.glyphs, g => f.glyph_id[g.code]);
const kernSorted = [];
for (let g of glyphs) {
if (!g.kerning || !Object.keys(g.kerning).length) continue;
const glyph_id = f.glyph_id[g.code];
const paired = u.sort_by(Object.keys(g.kerning), code => f.glyph_id[code]);
for (let code of paired) {
const glyph_id2 = f.glyph_id[code];
kernSorted.push([ glyph_id, glyph_id2, g.kerning[code] ]);
}
}
return kernSorted;
}
create_format0_data() {
const f = this.font;
const glyphs = this.font.src.glyphs;
const kernSorted = this.collect_format0_data();
const count = kernSorted.length;
const kerned_glyphs = glyphs.filter(g => Object.keys(g.kerning).length).length;
const kerning_list_max = Math.max(...glyphs.map(g => Object.keys(g.kerning).length));
debug(`${kerned_glyphs} kerned glyphs of ${glyphs.length}, ${kerning_list_max} max list, ${count} total pairs`);
const subheader = Buffer.alloc(4);
subheader.writeUInt32LE(count, 0);
const pairs_buf = Buffer.alloc((f.glyphIdFormat ? 4 : 2) * count);
// Write kerning pairs
for (let i = 0; i < count; i++) {
if (f.glyphIdFormat === 0) {
pairs_buf.writeUInt8(kernSorted[i][0], 2 * i);
pairs_buf.writeUInt8(kernSorted[i][1], 2 * i + 1);
} else {
pairs_buf.writeUInt16LE(kernSorted[i][0], 4 * i);
pairs_buf.writeUInt16LE(kernSorted[i][1], 4 * i + 2);
}
}
const values_buf = Buffer.alloc(count);
// Write kerning values
for (let i = 0; i < count; i++) {
values_buf.writeInt8(f.kernToFP(kernSorted[i][2]), i); // FP4.4
}
let buf = Buffer.concat([
subheader,
pairs_buf,
values_buf
]);
let buf_aligned = u.balign4(buf);
debug(`table format0 size = ${buf_aligned.length}`);
return buf_aligned;
}
collect_format3_data() {
const f = this.font;
const glyphs = u.sort_by(this.font.src.glyphs, g => f.glyph_id[g.code]);
// extract kerning pairs for each character;
// left kernings are kerning values based on left char (already there),
// right kernings are kerning values based on right char (extracted from left)
const left_kernings = {};
const right_kernings = {};
for (let g of glyphs) {
if (!g.kerning || !Object.keys(g.kerning).length) continue;
const paired = Object.keys(g.kerning);
left_kernings[g.code] = g.kerning;
for (let code of paired) {
right_kernings[code] = right_kernings[code] || {};
right_kernings[code][g.code] = g.kerning[code];
}
}
// input:
// - kernings, char => { hash: String, [char1]: Number, [char2]: Number, ... }
//
// returns:
// - array of [ char1, char2, ... ]
//
function build_classes(kernings) {
const classes = [];
for (let code of Object.keys(kernings)) {
// for each kerning table calculate unique value representing it;
// keys needs to be sorted for this (but we're using numeric keys, so
// sorting happens automatically and can't be changed)
const hash = JSON.stringify(kernings[code]);
classes[hash] = classes[hash] || [];
classes[hash].push(Number(code));
}
return Object.values(classes);
}
const left_classes = build_classes(left_kernings);
debug(`unique left classes: ${left_classes.length}`);
const right_classes = build_classes(right_kernings);
debug(`unique right classes: ${right_classes.length}`);
if (left_classes.length >= 255 || right_classes.length >= 255) {
debug('too many classes for format3 subtable');
return null;
}
function kern_class_mapping(classes) {
const arr = Array(f.last_id).fill(0);
classes.forEach((members, idx) => {
for (let code of members) {
arr[f.glyph_id[code]] = idx + 1;
}
});
return arr;
}
function kern_class_values() {
const arr = [];
for (let left_class of left_classes) {
for (let right_class of right_classes) {
let code1 = left_class[0];
let code2 = right_class[0];
arr.push(left_kernings[code1][code2] || 0);
}
}
return arr;
}
return {
left_classes: left_classes.length,
right_classes: right_classes.length,
left_mapping: kern_class_mapping(left_classes),
right_mapping: kern_class_mapping(right_classes),
values: kern_class_values()
};
}
create_format3_data() {
const f = this.font;
const {
left_classes,
right_classes,
left_mapping,
right_mapping,
values
} = this.collect_format3_data();
const subheader = Buffer.alloc(4);
subheader.writeUInt16LE(f.last_id);
subheader.writeUInt8(left_classes, 2);
subheader.writeUInt8(right_classes, 3);
let buf = Buffer.concat([
subheader,
Buffer.from(left_mapping),
Buffer.from(right_mapping),
Buffer.from(values.map(v => f.kernToFP(v)))
]);
let buf_aligned = u.balign4(buf);
debug(`table format3 size = ${buf_aligned.length}`);
return buf_aligned;
}
should_use_format3() {
if (!this.font.hasKerning()) return false;
const format0_data = this.create_format0_data();
const format3_data = this.create_format3_data();
if (format3_data && format3_data.length <= format0_data.length) return true;
if (this.font.opts.fast_kerning && format3_data) {
this.format3_forced = true;
return true;
}
return false;
}
toBin() {
if (!this.font.hasKerning()) return Buffer.alloc(0);
const format0_data = this.create_format0_data();
const format3_data = this.create_format3_data();
let header = Buffer.alloc(HEAD_LENGTH);
let data = format0_data;
header.writeUInt8(0, O_FORMAT);
/* eslint-disable no-console */
if (this.should_use_format3()) {
data = format3_data;
header.writeUInt8(3, O_FORMAT);
if (this.format3_forced) {
let diff = format3_data.length - format0_data.length;
console.log(`Forced faster kerning format (via classes). Size increase is ${diff} bytes.`);
}
} else if (this.font.opts.fast_kerning) {
console.log('Forced faster kerning format (via classes), but data exceeds it\'s limits. Continue use pairs.');
}
header.writeUInt32LE(header.length + data.length, O_SIZE);
header.write(this.label, O_LABEL);
return Buffer.concat([ header, data ]);
}
}
module.exports = Kern;

View File

@ -1,42 +0,0 @@
'use strict';
const u = require('../utils');
const debug = require('debug')('font.table.loca');
const O_SIZE = 0;
const O_LABEL = O_SIZE + 4;
const O_COUNT = O_LABEL + 4;
const HEAD_LENGTH = O_COUNT + 4;
class Loca {
constructor(font) {
this.font = font;
this.label = 'loca';
}
toBin() {
const f = this.font;
const offsets = [ ...Array(f.last_id).keys() ].map(i => f.glyf.getOffset(i));
const buf = u.balign4(Buffer.concat([
Buffer.alloc(HEAD_LENGTH),
f.indexToLocFormat ? u.bFromA32(offsets) : u.bFromA16(offsets)
]));
buf.writeUInt32LE(buf.length, O_SIZE);
buf.write(this.label, O_LABEL);
buf.writeUInt32LE(f.last_id, O_COUNT);
debug(`table size = ${buf.length}`);
return buf;
}
}
module.exports = Loca;

View File

@ -1,317 +0,0 @@
'use strict';
const ft_render_fabric = require('./build/ft_render');
let m = null; // compiled module instance
let library = 0; // pointer to library struct in wasm memory
// workaround because of bug in emscripten:
// https://github.com/emscripten-core/emscripten/issues/5820
const runtime_initialized = new Promise(resolve => {
ft_render_fabric().then(module_instance => {
m = module_instance;
resolve();
});
});
function from_16_16(fixed_point) {
return fixed_point / (1 << 16);
}
function from_26_6(fixed_point) {
return fixed_point / (1 << 6);
}
function int8_to_uint8(value) {
return value >= 0 ? value : value + 0x100;
}
let FT_New_Memory_Face,
FT_Set_Char_Size,
FT_Set_Pixel_Sizes,
FT_Get_Char_Index,
FT_Load_Glyph,
FT_Get_Sfnt_Table,
FT_Get_Kerning,
FT_Done_Face;
module.exports.init = async function () {
await runtime_initialized;
m._init_constants();
FT_New_Memory_Face = module.exports.FT_New_Memory_Face =
m.cwrap('FT_New_Memory_Face', 'number', [ 'number', 'number', 'number', 'number', 'number' ]);
FT_Set_Char_Size = module.exports.FT_Set_Char_Size =
m.cwrap('FT_Set_Char_Size', 'number', [ 'number', 'number', 'number', 'number', 'number' ]);
FT_Set_Pixel_Sizes = module.exports.FT_Set_Pixel_Sizes =
m.cwrap('FT_Set_Pixel_Sizes', 'number', [ 'number', 'number', 'number' ]);
FT_Get_Char_Index = module.exports.FT_Get_Char_Index =
m.cwrap('FT_Get_Char_Index', 'number', [ 'number', 'number' ]);
FT_Load_Glyph = module.exports.FT_Load_Glyph =
m.cwrap('FT_Load_Glyph', 'number', [ 'number', 'number', 'number' ]);
FT_Get_Sfnt_Table = module.exports.FT_Get_Sfnt_Table =
m.cwrap('FT_Get_Sfnt_Table', 'number', [ 'number', 'number' ]);
FT_Get_Kerning = module.exports.FT_Get_Kerning =
m.cwrap('FT_Get_Kerning', 'number', [ 'number', 'number', 'number', 'number', 'number' ]);
FT_Done_Face = module.exports.FT_Done_Face =
m.cwrap('FT_Done_Face', 'number', [ 'number' ]);
if (!library) {
let ptr = m._malloc(4);
try {
let error = m.ccall('FT_Init_FreeType', 'number', [ 'number' ], [ ptr ]);
if (error) throw new Error(`error in FT_Init_FreeType: ${error}`);
library = m.getValue(ptr, 'i32');
} finally {
m._free(ptr);
}
}
};
module.exports.fontface_create = function (source, size) {
let error;
let face = {
ptr: 0,
font: m._malloc(source.length)
};
m.writeArrayToMemory(source, face.font);
let ptr = m._malloc(4);
try {
error = FT_New_Memory_Face(library, face.font, source.length, 0, ptr);
if (error) throw new Error(`error in FT_New_Memory_Face: ${error}`);
face.ptr = m.getValue(ptr, 'i32');
} finally {
m._free(ptr);
}
error = FT_Set_Char_Size(face.ptr, 0, size * 64, 300, 300);
if (error) throw new Error(`error in FT_Set_Char_Size: ${error}`);
error = FT_Set_Pixel_Sizes(face.ptr, 0, size);
if (error) throw new Error(`error in FT_Set_Pixel_Sizes: ${error}`);
let units_per_em = m.getValue(face.ptr + m.OFFSET_FACE_UNITS_PER_EM, 'i16');
let ascender = m.getValue(face.ptr + m.OFFSET_FACE_ASCENDER, 'i16');
let descender = m.getValue(face.ptr + m.OFFSET_FACE_DESCENDER, 'i16');
let height = m.getValue(face.ptr + m.OFFSET_FACE_HEIGHT, 'i16');
return Object.assign(face, {
units_per_em,
ascender,
descender,
height
});
};
module.exports.fontface_os2_table = function (face) {
let sfnt_ptr = FT_Get_Sfnt_Table(face.ptr, m.FT_SFNT_OS2);
if (!sfnt_ptr) throw new Error('os/2 table not found for this font');
let typoAscent = m.getValue(sfnt_ptr + m.OFFSET_TT_OS2_ASCENDER, 'i16');
let typoDescent = m.getValue(sfnt_ptr + m.OFFSET_TT_OS2_DESCENDER, 'i16');
let typoLineGap = m.getValue(sfnt_ptr + m.OFFSET_TT_OS2_LINEGAP, 'i16');
return {
typoAscent,
typoDescent,
typoLineGap
};
};
module.exports.get_kerning = function (face, code1, code2) {
let glyph1 = FT_Get_Char_Index(face.ptr, code1);
let glyph2 = FT_Get_Char_Index(face.ptr, code2);
let ptr = m._malloc(4 * 2);
try {
let error = FT_Get_Kerning(face.ptr, glyph1, glyph2, m.FT_KERNING_DEFAULT, ptr);
if (error) throw new Error(`error in FT_Get_Kerning: ${error}`);
} finally {
m._free(ptr);
}
return {
x: from_26_6(m.getValue(ptr, 'i32')),
y: from_26_6(m.getValue(ptr + 4, 'i32'))
};
};
module.exports.glyph_exists = function (face, code) {
let glyph_index = FT_Get_Char_Index(face.ptr, code);
return glyph_index !== 0;
};
module.exports.glyph_render = function (face, code, opts = {}) {
let glyph_index = FT_Get_Char_Index(face.ptr, code);
if (glyph_index === 0) throw new Error(`glyph does not exist for codepoint ${code}`);
let load_flags = m.FT_LOAD_RENDER;
if (opts.mono) {
load_flags |= m.FT_LOAD_TARGET_MONO;
} else if (opts.lcd) {
load_flags |= m.FT_LOAD_TARGET_LCD;
} else if (opts.lcd_v) {
load_flags |= m.FT_LOAD_TARGET_LCD_V;
} else {
/* eslint-disable no-lonely-if */
// Use "light" by default, it changes horizontal lines only.
// "normal" is more strong (with vertical lines), but will break kerning, if
// no additional care taken. More advanced rendering requires upper level
// layout support (via Harfbuzz, for example).
if (!opts.autohint_strong) load_flags |= m.FT_LOAD_TARGET_LIGHT;
else load_flags |= m.FT_LOAD_TARGET_NORMAL;
}
if (opts.autohint_off) load_flags |= m.FT_LOAD_NO_AUTOHINT;
else load_flags |= m.FT_LOAD_FORCE_AUTOHINT;
if (opts.use_color_info) load_flags |= m.FT_LOAD_COLOR;
let error = FT_Load_Glyph(face.ptr, glyph_index, load_flags);
if (error) throw new Error(`error in FT_Load_Glyph: ${error}`);
let glyph = m.getValue(face.ptr + m.OFFSET_FACE_GLYPH, 'i32');
let glyph_data = {
glyph_index: m.getValue(glyph + m.OFFSET_GLYPH_INDEX, 'i32'),
metrics: {
width: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_WIDTH, 'i32')),
height: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_HEIGHT, 'i32')),
horiBearingX: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_HORI_BEARING_X, 'i32')),
horiBearingY: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_HORI_BEARING_Y, 'i32')),
horiAdvance: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_HORI_ADVANCE, 'i32')),
vertBearingX: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_VERT_BEARING_X, 'i32')),
vertBearingY: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_VERT_BEARING_Y, 'i32')),
vertAdvance: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_METRICS_VERT_ADVANCE, 'i32'))
},
linearHoriAdvance: from_16_16(m.getValue(glyph + m.OFFSET_GLYPH_LINEAR_HORI_ADVANCE, 'i32')),
linearVertAdvance: from_16_16(m.getValue(glyph + m.OFFSET_GLYPH_LINEAR_VERT_ADVANCE, 'i32')),
advance: {
x: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_ADVANCE_X, 'i32')),
y: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_ADVANCE_Y, 'i32'))
},
bitmap: {
width: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_WIDTH, 'i32'),
rows: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_ROWS, 'i32'),
pitch: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_PITCH, 'i32'),
num_grays: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_NUM_GRAYS, 'i16'),
pixel_mode: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_PIXEL_MODE, 'i8'),
palette_mode: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_PALETTE_MODE, 'i8')
},
bitmap_left: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_LEFT, 'i32'),
bitmap_top: m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_TOP, 'i32'),
lsb_delta: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_LSB_DELTA, 'i32')),
rsb_delta: from_26_6(m.getValue(glyph + m.OFFSET_GLYPH_RSB_DELTA, 'i32'))
};
let g_w = glyph_data.bitmap.width;
let g_h = glyph_data.bitmap.rows;
let g_x = glyph_data.bitmap_left;
let g_y = glyph_data.bitmap_top;
let buffer = m.getValue(glyph + m.OFFSET_GLYPH_BITMAP_BUFFER, 'i32');
let pitch = Math.abs(glyph_data.bitmap.pitch);
let advance_x = glyph_data.linearHoriAdvance;
let advance_y = glyph_data.linearVertAdvance;
let pixel_mode = glyph_data.bitmap.pixel_mode;
let output = [];
for (let y = 0; y < g_h; y++) {
let row_start = buffer + y * pitch;
let line = [];
for (let x = 0; x < g_w; x++) {
if (pixel_mode === m.FT_PIXEL_MODE_MONO) {
let value = m.getValue(row_start + ~~(x / 8), 'i8');
line.push(value & (1 << (7 - (x % 8))) ? 255 : 0);
} else if (pixel_mode === m.FT_PIXEL_MODE_BGRA) {
let blue = int8_to_uint8(m.getValue(row_start + (x * 4) + 0, 'i8'));
let green = int8_to_uint8(m.getValue(row_start + (x * 4) + 1, 'i8'));
let red = int8_to_uint8(m.getValue(row_start + (x * 4) + 2, 'i8'));
let alpha = int8_to_uint8(m.getValue(row_start + (x * 4) + 3, 'i8'));
// convert RGBA to grayscale
let grayscale = Math.round(0.299 * red + 0.587 * green + 0.114 * blue);
if (grayscale > 255) grayscale = 255;
// meld grayscale into alpha channel
alpha = ((255 - grayscale) * alpha) / 255;
line.push(alpha);
} else {
let value = m.getValue(row_start + x, 'i8');
line.push(int8_to_uint8(value));
}
}
output.push(line);
}
return {
x: g_x,
y: g_y,
width: g_w,
height: g_h,
advance_x,
advance_y,
pixels: output,
freetype: glyph_data
};
};
module.exports.fontface_destroy = function (face) {
let error = FT_Done_Face(face.ptr);
if (error) throw new Error(`error in FT_Done_Face: ${error}`);
m._free(face.font);
face.ptr = 0;
face.font = 0;
};
module.exports.destroy = function () {
let error = m.ccall('FT_Done_FreeType', 'number', [ 'number' ], [ library ]);
if (error) throw new Error(`error in FT_Done_FreeType: ${error}`);
library = 0;
// don't unload wasm - slows down tests too much
//m = null;
};

View File

@ -1,83 +0,0 @@
#include <emscripten.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
static void set_js_variable(char* name, int value) {
char buffer[strlen(name) + 32];
sprintf(buffer, "Module.%s = %d;", name, value);
emscripten_run_script(buffer);
}
// Expose constants, used in calls from js
void init_constants()
{
set_js_variable("FT_LOAD_DEFAULT", FT_LOAD_DEFAULT);
set_js_variable("FT_LOAD_NO_HINTING", FT_LOAD_NO_HINTING);
set_js_variable("FT_LOAD_RENDER", FT_LOAD_RENDER);
set_js_variable("FT_LOAD_FORCE_AUTOHINT", FT_LOAD_FORCE_AUTOHINT);
set_js_variable("FT_LOAD_PEDANTIC", FT_LOAD_PEDANTIC);
set_js_variable("FT_LOAD_MONOCHROME", FT_LOAD_MONOCHROME);
set_js_variable("FT_LOAD_NO_AUTOHINT", FT_LOAD_NO_AUTOHINT);
set_js_variable("FT_LOAD_COLOR", FT_LOAD_COLOR);
set_js_variable("FT_LOAD_TARGET_NORMAL", FT_LOAD_TARGET_NORMAL);
set_js_variable("FT_LOAD_TARGET_LIGHT", FT_LOAD_TARGET_LIGHT);
set_js_variable("FT_LOAD_TARGET_MONO", FT_LOAD_TARGET_MONO);
set_js_variable("FT_LOAD_TARGET_LCD", FT_LOAD_TARGET_LCD);
set_js_variable("FT_LOAD_TARGET_LCD_V", FT_LOAD_TARGET_LCD_V);
set_js_variable("FT_RENDER_MODE_NORMAL", FT_RENDER_MODE_NORMAL);
set_js_variable("FT_RENDER_MODE_MONO", FT_RENDER_MODE_MONO);
set_js_variable("FT_RENDER_MODE_LCD", FT_RENDER_MODE_LCD);
set_js_variable("FT_RENDER_MODE_LCD_V", FT_RENDER_MODE_LCD_V);
set_js_variable("FT_KERNING_DEFAULT", FT_KERNING_DEFAULT);
set_js_variable("FT_KERNING_UNFITTED", FT_KERNING_UNFITTED);
set_js_variable("FT_KERNING_UNSCALED", FT_KERNING_UNSCALED);
set_js_variable("FT_SFNT_OS2", FT_SFNT_OS2);
set_js_variable("FT_FACE_FLAG_COLOR", FT_FACE_FLAG_COLOR);
set_js_variable("FT_PIXEL_MODE_MONO", FT_PIXEL_MODE_MONO);
set_js_variable("FT_PIXEL_MODE_BGRA", FT_PIXEL_MODE_BGRA);
set_js_variable("OFFSET_FACE_GLYPH", offsetof(FT_FaceRec, glyph));
set_js_variable("OFFSET_FACE_UNITS_PER_EM", offsetof(FT_FaceRec, units_per_EM));
set_js_variable("OFFSET_FACE_ASCENDER", offsetof(FT_FaceRec, ascender));
set_js_variable("OFFSET_FACE_DESCENDER", offsetof(FT_FaceRec, descender));
set_js_variable("OFFSET_FACE_HEIGHT", offsetof(FT_FaceRec, height));
set_js_variable("OFFSET_FACE_FACE_FLAGS", offsetof(FT_FaceRec, face_flags));
set_js_variable("OFFSET_GLYPH_BITMAP_WIDTH", offsetof(FT_GlyphSlotRec, bitmap.width));
set_js_variable("OFFSET_GLYPH_BITMAP_ROWS", offsetof(FT_GlyphSlotRec, bitmap.rows));
set_js_variable("OFFSET_GLYPH_BITMAP_PITCH", offsetof(FT_GlyphSlotRec, bitmap.pitch));
set_js_variable("OFFSET_GLYPH_BITMAP_BUFFER", offsetof(FT_GlyphSlotRec, bitmap.buffer));
set_js_variable("OFFSET_GLYPH_BITMAP_NUM_GRAYS", offsetof(FT_GlyphSlotRec, bitmap.num_grays));
set_js_variable("OFFSET_GLYPH_BITMAP_PIXEL_MODE", offsetof(FT_GlyphSlotRec, bitmap.pixel_mode));
set_js_variable("OFFSET_GLYPH_BITMAP_PALETTE_MODE", offsetof(FT_GlyphSlotRec, bitmap.palette_mode));
set_js_variable("OFFSET_GLYPH_METRICS_WIDTH", offsetof(FT_GlyphSlotRec, metrics.width));
set_js_variable("OFFSET_GLYPH_METRICS_HEIGHT", offsetof(FT_GlyphSlotRec, metrics.height));
set_js_variable("OFFSET_GLYPH_METRICS_HORI_BEARING_X", offsetof(FT_GlyphSlotRec, metrics.horiBearingX));
set_js_variable("OFFSET_GLYPH_METRICS_HORI_BEARING_Y", offsetof(FT_GlyphSlotRec, metrics.horiBearingY));
set_js_variable("OFFSET_GLYPH_METRICS_HORI_ADVANCE", offsetof(FT_GlyphSlotRec, metrics.horiAdvance));
set_js_variable("OFFSET_GLYPH_METRICS_VERT_BEARING_X", offsetof(FT_GlyphSlotRec, metrics.vertBearingX));
set_js_variable("OFFSET_GLYPH_METRICS_VERT_BEARING_Y", offsetof(FT_GlyphSlotRec, metrics.vertBearingY));
set_js_variable("OFFSET_GLYPH_METRICS_VERT_ADVANCE", offsetof(FT_GlyphSlotRec, metrics.vertAdvance));
set_js_variable("OFFSET_GLYPH_BITMAP_LEFT", offsetof(FT_GlyphSlotRec, bitmap_left));
set_js_variable("OFFSET_GLYPH_BITMAP_TOP", offsetof(FT_GlyphSlotRec, bitmap_top));
set_js_variable("OFFSET_GLYPH_INDEX", offsetof(FT_GlyphSlotRec, glyph_index));
set_js_variable("OFFSET_GLYPH_LINEAR_HORI_ADVANCE", offsetof(FT_GlyphSlotRec, linearHoriAdvance));
set_js_variable("OFFSET_GLYPH_LINEAR_VERT_ADVANCE", offsetof(FT_GlyphSlotRec, linearVertAdvance));
set_js_variable("OFFSET_GLYPH_ADVANCE_X", offsetof(FT_GlyphSlotRec, advance.x));
set_js_variable("OFFSET_GLYPH_ADVANCE_Y", offsetof(FT_GlyphSlotRec, advance.y));
set_js_variable("OFFSET_GLYPH_LSB_DELTA", offsetof(FT_GlyphSlotRec, lsb_delta));
set_js_variable("OFFSET_GLYPH_RSB_DELTA", offsetof(FT_GlyphSlotRec, rsb_delta));
set_js_variable("OFFSET_TT_OS2_ASCENDER", offsetof(TT_OS2, sTypoAscender));
set_js_variable("OFFSET_TT_OS2_DESCENDER", offsetof(TT_OS2, sTypoDescender));
set_js_variable("OFFSET_TT_OS2_LINEGAP", offsetof(TT_OS2, sTypoLineGap));
}

View File

@ -1,51 +0,0 @@
// Merge ranges into single object
'use strict';
class Ranger {
constructor() {
this.data = {};
}
// input:
// -r 0x1F450 - single value, dec or hex format
// -r 0x1F450-0x1F470 - range
// -r 0x1F450=>0xF005 - single glyph with mapping
// -r 0x1F450-0x1F470=>0xF005 - range with mapping
add_range(font, start, end, mapped_start) {
let offset = mapped_start - start;
let output = [];
for (let i = start; i <= end; i++) {
this._set_char(font, i, i + offset);
output.push(i);
}
return output;
}
// input: characters to copy, e.g. '1234567890abcdef'
add_symbols(font, str) {
let output = [];
for (let chr of str) {
let code = chr.codePointAt(0);
this._set_char(font, code, code);
output.push(code);
}
return output;
}
_set_char(font, code, mapped_to) {
this.data[mapped_to] = { font, code };
}
get() {
return this.data;
}
}
module.exports = Ranger;

View File

@ -1,131 +0,0 @@
'use strict';
function set_byte_depth(depth) {
return function (byte) {
// calculate significant bits, e.g. for depth=2 it's 0, 1, 2 or 3
let value = ~~(byte / (256 >> depth));
// spread those bits around 0..255 range, e.g. for depth=2 it's 0, 85, 170 or 255
let scale = (2 << (depth - 1)) - 1;
return (value * 0xFFFF / scale) >> 8;
};
}
module.exports.set_depth = function set_depth(glyph, depth) {
let pixels = [];
let fn = set_byte_depth(depth);
for (let y = 0; y < glyph.bbox.height; y++) {
pixels.push(glyph.pixels[y].map(fn));
}
return Object.assign({}, glyph, { pixels });
};
function count_bits(val) {
let count = 0;
val = ~~val;
while (val) {
count++;
val >>= 1;
}
return count;
}
// Minimal number of bits to store unsigned value
module.exports.unsigned_bits = count_bits;
// Minimal number of bits to store signed value
module.exports.signed_bits = function signed_bits(val) {
if (val >= 0) return count_bits(val) + 1;
return count_bits(Math.abs(val) - 1) + 1;
};
// Align value to 4x - useful to create word-aligned arrays
function align4(size) {
if (size % 4 === 0) return size;
return size + 4 - (size % 4);
}
module.exports.align4 = align4;
// Align buffer length to 4x (returns copy with zero-filled tail)
module.exports.balign4 = function balign4(buf) {
let buf_aligned = Buffer.alloc(align4(buf.length));
buf.copy(buf_aligned);
return buf_aligned;
};
// Pre-filter image to improve compression ratio
// In this case - XOR lines, because it's very effective
// in decompressor and does not depend on bpp.
module.exports.prefilter = function prefilter(pixels) {
return pixels.map((line, l_idx, arr) => {
if (l_idx === 0) return line.slice();
return line.map((p, idx) => p ^ arr[l_idx - 1][idx]);
});
};
// Convert array with uint16 data to buffer
module.exports.bFromA16 = function bFromA16(arr) {
const buf = Buffer.alloc(arr.length * 2);
for (let i = 0; i < arr.length; i++) buf.writeUInt16LE(arr[i], i * 2);
return buf;
};
// Convert array with uint32 data to buffer
module.exports.bFromA32 = function bFromA32(arr) {
const buf = Buffer.alloc(arr.length * 4);
for (let i = 0; i < arr.length; i++) buf.writeUInt32LE(arr[i], i * 4);
return buf;
};
function chunk(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
// Dump long array to multiline format with X columns and Y indent
module.exports.long_dump = function long_dump(arr, options = {}) {
const defaults = {
col: 8,
indent: 4,
hex: false
};
let opts = Object.assign({}, defaults, options);
let indent = ' '.repeat(opts.indent);
return chunk(Array.from(arr), opts.col)
.map(l => l.map(v => (opts.hex ? `0x${v.toString(16)}` : v.toString())))
.map(l => `${indent}${l.join(', ')}`)
.join(',\n');
};
// stable sort by pick() result
module.exports.sort_by = function sort_by(arr, pick) {
return arr
.map((el, idx) => ({ el, idx }))
.sort((a, b) => (pick(a.el) - pick(b.el)) || (a.idx - b.idx))
.map(({ el }) => el);
};
module.exports.sum = function sum(arr) {
return arr.reduce((a, v) => a + v, 0);
};

View File

@ -1,17 +0,0 @@
// Write font in binary format
'use strict';
const AppError = require('../app_error');
const Font = require('../font/font');
module.exports = function write_images(args, fontData) {
if (!args.output) throw new AppError('Output is required for "bin" writer');
const font = new Font(fontData, args);
return {
[args.output]: font.toBin()
};
};

View File

@ -1,68 +0,0 @@
// Write font data into png images
'use strict';
const path = require('path');
const { PNG } = require('pngjs');
const AppError = require('../app_error');
const utils = require('../utils');
const normal_color = [ 255, 255, 255 ];
const outside_color = [ 255, 127, 184 ];
module.exports = function write_images(args, font) {
if (!args.output) throw new AppError('Output is required for "dump" writer');
let files = {};
let glyphs = font.glyphs.map(glyph => utils.set_depth(glyph, args.bpp));
for (let glyph of glyphs) {
let { code, advanceWidth, bbox, pixels } = glyph;
advanceWidth = Math.round(advanceWidth);
let minX = bbox.x;
let maxX = Math.max(bbox.x + bbox.width - 1, bbox.x);
let minY = Math.min(bbox.y, font.typoDescent);
let maxY = Math.max(bbox.y + bbox.height - 1, font.typoAscent);
let png = new PNG({ width: maxX - minX + 1, height: maxY - minY + 1 });
/* eslint-disable max-depth */
for (let pos = 0, y = maxY; y >= minY; y--) {
for (let x = minX; x <= maxX; x++) {
let value = 0;
if (x >= bbox.x && x < bbox.x + bbox.width && y >= bbox.y && y < bbox.y + bbox.height) {
value = pixels[bbox.height - (y - bbox.y) - 1][x - bbox.x];
}
let r, g, b;
if (x < 0 || x >= advanceWidth || y < font.typoDescent || y > font.typoAscent) {
[ r, g, b ] = outside_color;
} else {
[ r, g, b ] = normal_color;
}
png.data[pos++] = (255 - value) * r / 255;
png.data[pos++] = (255 - value) * g / 255;
png.data[pos++] = (255 - value) * b / 255;
png.data[pos++] = 255;
}
}
files[path.join(args.output, `${code.toString(16)}.png`)] = PNG.sync.write(png);
}
files[path.join(args.output, 'font_info.json')] = JSON.stringify(
font,
(k, v) => (k === 'pixels' && !args.full_info ? undefined : v),
2);
return files;
};

View File

@ -1,17 +0,0 @@
// Write font in lvgl format
'use strict';
const AppError = require('../../app_error');
const Font = require('./lv_font');
module.exports = function write_images(args, fontData) {
if (!args.output) throw new AppError('Output is required for "lvgl" writer');
const font = new Font(fontData, args);
return {
[args.output]: font.toLVGL()
};
};

View File

@ -1,98 +0,0 @@
'use strict';
const path = require('path');
const Font = require('../../font/font');
const Head = require('./lv_table_head');
const Cmap = require('./lv_table_cmap');
const Glyf = require('./lv_table_glyf');
const Kern = require('./lv_table_kern');
const AppError = require('../../app_error');
class LvFont extends Font {
constructor(fontData, options) {
super(fontData, options);
const ext = path.extname(options.output);
this.font_name = path.basename(options.output, ext);
if (options.bpp === 3 & options.no_compress) {
throw new AppError('LittlevGL supports "--bpp 3" with compression only');
}
}
init_tables() {
this.head = new Head(this);
this.glyf = new Glyf(this);
this.cmap = new Cmap(this);
this.kern = new Kern(this);
}
large_format_guard() {
let guard_required = false;
let glyphs_bin_size = 0;
this.glyf.lv_data.forEach(d => {
glyphs_bin_size += d.bin.length;
if (d.glyph.bbox.width > 255 ||
d.glyph.bbox.height > 255 ||
Math.abs(d.glyph.bbox.x) > 127 ||
Math.abs(d.glyph.bbox.y) > 127 ||
Math.round(d.glyph.advanceWidth * 16) > 4096) {
guard_required = true;
}
});
if (glyphs_bin_size > 1024 * 1024) guard_required = true;
if (!guard_required) return '';
return `
#if (LV_FONT_FMT_TXT_LARGE == 0)
# error "Too large font or glyphs in ${this.font_name.toUpperCase()}. Enable LV_FONT_FMT_TXT_LARGE in lv_conf.h")
#endif
`.trimLeft();
}
toLVGL() {
let guard_name = this.font_name.toUpperCase();
return `/*******************************************************************************
* Size: ${this.src.size} px
* Bpp: ${this.opts.bpp}
* Opts: ${process.argv.slice(2).join(' ')}
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "${this.opts.lv_include || 'lvgl/lvgl.h'}"
#endif
#ifndef ${guard_name}
#define ${guard_name} 1
#endif
#if ${guard_name}
${this.glyf.toLVGL()}
${this.cmap.toLVGL()}
${this.kern.toLVGL()}
${this.head.toLVGL()}
${this.large_format_guard()}
#endif /*#if ${guard_name}*/
`;
}
}
module.exports = LvFont;

View File

@ -1,125 +0,0 @@
'use strict';
const u = require('../../utils');
const build_subtables = require('../../font/cmap_build_subtables');
const Cmap = require('../../font/table_cmap');
class LvCmap extends Cmap {
constructor(font) {
super(font);
this.lv_compiled = false;
this.lv_subtables = [];
}
lv_format2enum(name) {
switch (name) {
case 'format0_tiny': return 'LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY';
case 'format0': return 'LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL';
case 'sparse_tiny': return 'LV_FONT_FMT_TXT_CMAP_SPARSE_TINY';
case 'sparse': return 'LV_FONT_FMT_TXT_CMAP_SPARSE_FULL';
default: throw new Error('Unknown subtable format');
}
}
lv_compile() {
if (this.lv_compiled) return;
this.lv_compiled = true;
const f = this.font;
let subtables_plan = build_subtables(f.src.glyphs.map(g => g.code));
let idx = 0;
for (let [ format, codepoints ] of subtables_plan) {
let g = this.glyphByCode(codepoints[0]);
let start_glyph_id = f.glyph_id[g.code];
let min_code = codepoints[0];
let max_code = codepoints[codepoints.length - 1];
let has_charcodes = false;
let has_ids = false;
let defs = '';
let entries_count = 0;
if (format === 'format0_tiny') {
// use default empty values
} else if (format === 'format0') {
has_ids = true;
let d = this.collect_format0_data(min_code, max_code, start_glyph_id);
entries_count = d.length;
defs = `
static const uint8_t glyph_id_ofs_list_${idx}[] = {
${u.long_dump(d)}
};
`.trim();
} else if (format === 'sparse_tiny') {
has_charcodes = true;
let d = this.collect_sparse_data(codepoints, start_glyph_id);
entries_count = d.codes.length;
defs = `
static const uint16_t unicode_list_${idx}[] = {
${u.long_dump(d.codes, { hex: true })}
};
`.trim();
} else { // assume format === 'sparse'
has_charcodes = true;
has_ids = true;
let d = this.collect_sparse_data(codepoints, start_glyph_id);
entries_count = d.codes.length;
defs = `
static const uint16_t unicode_list_${idx}[] = {
${u.long_dump(d.codes, { hex: true })}
};
static const uint16_t glyph_id_ofs_list_${idx}[] = {
${u.long_dump(d.ids)}
};
`.trim();
}
const u_list = has_charcodes ? `unicode_list_${idx}` : 'NULL';
const id_list = has_ids ? `glyph_id_ofs_list_${idx}` : 'NULL';
/* eslint-disable max-len */
const head = ` {
.range_start = ${min_code}, .range_length = ${max_code - min_code + 1}, .glyph_id_start = ${start_glyph_id},
.unicode_list = ${u_list}, .glyph_id_ofs_list = ${id_list}, .list_length = ${entries_count}, .type = ${this.lv_format2enum(format)}
}`;
this.lv_subtables.push({
defs,
head
});
idx++;
}
}
toLVGL() {
this.lv_compile();
return `
/*---------------------
* CHARACTER MAPPING
*--------------------*/
${this.lv_subtables.map(d => d.defs).filter(Boolean).join('\n\n')}
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
${this.lv_subtables.map(d => d.head).join(',\n')}
};
`.trim();
}
}
module.exports = LvCmap;

View File

@ -1,121 +0,0 @@
'use strict';
const { BitStream } = require('bit-buffer');
const u = require('../../utils');
const Glyf = require('../../font/table_glyf');
class LvGlyf extends Glyf {
constructor(font) {
super(font);
this.lv_data = [];
this.lv_compiled = false;
}
lv_bitmap(glyph) {
const buf = Buffer.alloc(100 + glyph.bbox.width * glyph.bbox.height * 4);
const bs = new BitStream(buf);
bs.bigEndian = true;
const pixels = this.font.glyf.pixelsToBpp(glyph.pixels);
this.font.glyf.storePixels(bs, pixels);
const glyph_bitmap = Buffer.alloc(bs.byteIndex);
buf.copy(glyph_bitmap, 0, 0, bs.byteIndex);
return glyph_bitmap;
}
lv_compile() {
if (this.lv_compiled) return;
this.lv_compiled = true;
const f = this.font;
this.lv_data = [];
let offset = 0;
f.src.glyphs.forEach(g => {
const id = f.glyph_id[g.code];
const bin = this.lv_bitmap(g);
this.lv_data[id] = {
bin,
offset,
glyph: g
};
offset += bin.length;
});
}
to_lv_bitmaps() {
this.lv_compile();
let result = [];
this.lv_data.forEach((d, idx) => {
if (idx === 0) return;
const code_hex = d.glyph.code.toString(16).toUpperCase();
const code_str = JSON.stringify(String.fromCodePoint(d.glyph.code));
let txt = ` /* U+${code_hex.padStart(4, '0')} ${code_str} */
${u.long_dump(d.bin, { hex: true })}`;
if (idx < this.lv_data.length - 1) {
// skip comma for zero data
txt += d.bin.length ? ',\n\n' : '\n';
}
result.push(txt);
});
return result.join('');
}
to_lv_glyph_dsc() {
this.lv_compile();
/* eslint-disable max-len */
let result = [ ' {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */' ];
this.lv_data.forEach(d => {
const idx = d.offset,
adv_w = Math.round(d.glyph.advanceWidth * 16),
h = d.glyph.bbox.height,
w = d.glyph.bbox.width,
x = d.glyph.bbox.x,
y = d.glyph.bbox.y;
result.push(` {.bitmap_index = ${idx}, .adv_w = ${adv_w}, .box_w = ${w}, .box_h = ${h}, .ofs_x = ${x}, .ofs_y = ${y}}`);
});
return result.join(',\n');
}
toLVGL() {
return `
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
${this.to_lv_bitmaps()}
};
/*---------------------
* GLYPH DESCRIPTION
*--------------------*/
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
${this.to_lv_glyph_dsc()}
};
`.trim();
}
}
module.exports = LvGlyf;

View File

@ -1,99 +0,0 @@
'use strict';
const Head = require('../../font/table_head');
class LvHead extends Head {
constructor(font) {
super(font);
}
kern_ref() {
const f = this.font;
if (!f.hasKerning()) {
return {
scale: '0',
dsc: 'NULL',
classes: '0'
};
}
if (!f.kern.should_use_format3()) {
return {
scale: `${Math.round(f.kerningScale * 16)}`,
dsc: '&kern_pairs',
classes: '0'
};
}
return {
scale: `${Math.round(f.kerningScale * 16)}`,
dsc: '&kern_classes',
classes: '1'
};
}
toLVGL() {
const f = this.font;
const kern = this.kern_ref();
const subpixels = (f.subpixels_mode === 0) ? 'LV_FONT_SUBPX_NONE' :
(f.subpixels_mode === 1) ? 'LV_FONT_SUBPX_HOR' : 'LV_FONT_SUBPX_VER';
return `
/*--------------------
* ALL CUSTOM DATA
*--------------------*/
#if LV_VERSION_CHECK(8, 0, 0)
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
static const lv_font_fmt_txt_dsc_t font_dsc = {
#else
static lv_font_fmt_txt_dsc_t font_dsc = {
#endif
.glyph_bitmap = glyph_bitmap,
.glyph_dsc = glyph_dsc,
.cmaps = cmaps,
.kern_dsc = ${kern.dsc},
.kern_scale = ${kern.scale},
.cmap_num = ${f.cmap.toBin().readUInt32LE(8)},
.bpp = ${f.opts.bpp},
.kern_classes = ${kern.classes},
.bitmap_format = ${f.glyf.getCompressionCode()},
#if LV_VERSION_CHECK(8, 0, 0)
.cache = &cache
#endif
};
/*-----------------
* PUBLIC FONT
*----------------*/
/*Initialize a public general font descriptor*/
#if LV_VERSION_CHECK(8, 0, 0)
const lv_font_t ${f.font_name} = {
#else
lv_font_t ${f.font_name} = {
#endif
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
.line_height = ${f.src.ascent - f.src.descent}, /*The maximum line height required by the font*/
.base_line = ${-f.src.descent}, /*Baseline measured from the bottom of the line*/
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
.subpx = ${subpixels},
#endif
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
.underline_position = ${f.src.underlinePosition},
.underline_thickness = ${f.src.underlineThickness},
#endif
.dsc = &font_dsc /*The custom font data. Will be accessed by \`get_glyph_bitmap/dsc\` */
};
`.trim();
}
}
module.exports = LvHead;

View File

@ -1,121 +0,0 @@
'use strict';
const u = require('../../utils');
const Kern = require('../../font/table_kern');
class LvKern extends Kern {
constructor(font) {
super(font);
}
to_lv_format0() {
const f = this.font;
let kern_pairs = this.collect_format0_data();
return `
/*-----------------
* KERNING
*----------------*/
/*Pair left and right glyphs for kerning*/
static const ${f.glyphIdFormat ? 'uint16_t' : 'uint8_t'} kern_pair_glyph_ids[] =
{
${kern_pairs.map(pair => ` ${pair[0]}, ${pair[1]}`).join(',\n')}
};
/* Kerning between the respective left and right glyphs
* 4.4 format which needs to scaled with \`kern_scale\`*/
static const int8_t kern_pair_values[] =
{
${u.long_dump(kern_pairs.map(pair => f.kernToFP(pair[2])))}
};
/*Collect the kern pair's data in one place*/
static const lv_font_fmt_txt_kern_pair_t kern_pairs =
{
.glyph_ids = kern_pair_glyph_ids,
.values = kern_pair_values,
.pair_cnt = ${kern_pairs.length},
.glyph_ids_size = ${f.glyphIdFormat}
};
`.trim();
}
to_lv_format3() {
const f = this.font;
const {
left_classes,
right_classes,
left_mapping,
right_mapping,
values
} = this.collect_format3_data();
return `
/*-----------------
* KERNING
*----------------*/
/*Map glyph_ids to kern left classes*/
static const uint8_t kern_left_class_mapping[] =
{
${u.long_dump(left_mapping)}
};
/*Map glyph_ids to kern right classes*/
static const uint8_t kern_right_class_mapping[] =
{
${u.long_dump(right_mapping)}
};
/*Kern values between classes*/
static const int8_t kern_class_values[] =
{
${u.long_dump(values.map(v => f.kernToFP(v)))}
};
/*Collect the kern class' data in one place*/
static const lv_font_fmt_txt_kern_classes_t kern_classes =
{
.class_pair_values = kern_class_values,
.left_class_mapping = kern_left_class_mapping,
.right_class_mapping = kern_right_class_mapping,
.left_class_cnt = ${left_classes},
.right_class_cnt = ${right_classes},
};
`.trim();
}
toLVGL() {
const f = this.font;
if (!f.hasKerning()) return '';
/* eslint-disable no-console */
if (f.kern.should_use_format3()) {
if (f.kern.format3_forced) {
let diff = this.create_format3_data().length - this.create_format0_data().length;
console.log(`Forced faster kerning format (via classes). Size increase is ${diff} bytes.`);
}
return this.to_lv_format3();
}
if (this.font.opts.fast_kerning) {
console.log('Forced faster kerning format (via classes), but data exceeds it\'s limits. Continue use pairs.');
}
return this.to_lv_format0();
}
}
module.exports = LvKern;

View File

@ -1,17 +0,0 @@
#!/usr/bin/env node
'use strict';
const AppError = require('./lib/app_error');
require('./lib/cli').run(process.argv.slice(2)).catch(err => {
/*eslint-disable no-console*/
if (err instanceof AppError) {
// Try to beautify normal errors
console.error(err.message.trim());
} else {
// Print crashes
console.error(err.stack);
}
process.exit(1);
});

View File

@ -1,216 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.1] - 2020-08-29
### Fixed
- Fix issue with `process.argv` when used with interpreters (`coffee`, `ts-node`, etc.), #150.
## [2.0.0] - 2020-08-14
### Changed
- Full rewrite. Now port from python 3.9.0 & more precise following.
See [doc](./doc) for difference and migration info.
- node.js 10+ required
- Removed most of local docs in favour of original ones.
## [1.0.10] - 2018-02-15
### Fixed
- Use .concat instead of + for arrays, #122.
## [1.0.9] - 2016-09-29
### Changed
- Rerelease after 1.0.8 - deps cleanup.
## [1.0.8] - 2016-09-29
### Changed
- Maintenance (deps bump, fix node 6.5+ tests, coverage report).
## [1.0.7] - 2016-03-17
### Changed
- Teach `addArgument` to accept string arg names. #97, @tomxtobin.
## [1.0.6] - 2016-02-06
### Changed
- Maintenance: moved to eslint & updated CS.
## [1.0.5] - 2016-02-05
### Changed
- Removed lodash dependency to significantly reduce install size.
Thanks to @mourner.
## [1.0.4] - 2016-01-17
### Changed
- Maintenance: lodash update to 4.0.0.
## [1.0.3] - 2015-10-27
### Fixed
- Fix parse `=` in args: `--examplepath="C:\myfolder\env=x64"`. #84, @CatWithApple.
## [1.0.2] - 2015-03-22
### Changed
- Relaxed lodash version dependency.
## [1.0.1] - 2015-02-20
### Changed
- Changed dependencies to be compatible with ancient nodejs.
## [1.0.0] - 2015-02-19
### Changed
- Maintenance release.
- Replaced `underscore` with `lodash`.
- Bumped version to 1.0.0 to better reflect semver meaning.
- HISTORY.md -> CHANGELOG.md
## [0.1.16] - 2013-12-01
### Changed
- Maintenance release. Updated dependencies and docs.
## [0.1.15] - 2013-05-13
### Fixed
- Fixed #55, @trebor89
## [0.1.14] - 2013-05-12
### Fixed
- Fixed #62, @maxtaco
## [0.1.13] - 2013-04-08
### Changed
- Added `.npmignore` to reduce package size
## [0.1.12] - 2013-02-10
### Fixed
- Fixed conflictHandler (#46), @hpaulj
## [0.1.11] - 2013-02-07
### Added
- Added 70+ tests (ported from python), @hpaulj
- Added conflictHandler, @applepicke
- Added fromfilePrefixChar, @hpaulj
### Fixed
- Multiple bugfixes, @hpaulj
## [0.1.10] - 2012-12-30
### Added
- Added [mutual exclusion](http://docs.python.org/dev/library/argparse.html#mutual-exclusion)
support, thanks to @hpaulj
### Fixed
- Fixed options check for `storeConst` & `appendConst` actions, thanks to @hpaulj
## [0.1.9] - 2012-12-27
### Fixed
- Fixed option dest interferens with other options (issue #23), thanks to @hpaulj
- Fixed default value behavior with `*` positionals, thanks to @hpaulj
- Improve `getDefault()` behavior, thanks to @hpaulj
- Improve negative argument parsing, thanks to @hpaulj
## [0.1.8] - 2012-12-01
### Fixed
- Fixed parser parents (issue #19), thanks to @hpaulj
- Fixed negative argument parse (issue #20), thanks to @hpaulj
## [0.1.7] - 2012-10-14
### Fixed
- Fixed 'choices' argument parse (issue #16)
- Fixed stderr output (issue #15)
## [0.1.6] - 2012-09-09
### Fixed
- Fixed check for conflict of options (thanks to @tomxtobin)
## [0.1.5] - 2012-09-03
### Fixed
- Fix parser #setDefaults method (thanks to @tomxtobin)
## [0.1.4] - 2012-07-30
### Fixed
- Fixed pseudo-argument support (thanks to @CGamesPlay)
- Fixed addHelp default (should be true), if not set (thanks to @benblank)
## [0.1.3] - 2012-06-27
### Fixed
- Fixed formatter api name: Formatter -> HelpFormatter
## [0.1.2] - 2012-05-29
### Fixed
- Removed excess whitespace in help
- Fixed error reporting, when parcer with subcommands
called with empty arguments
### Added
- Added basic tests
## [0.1.1] - 2012-05-23
### Fixed
- Fixed line wrapping in help formatter
- Added better error reporting on invalid arguments
## [0.1.0] - 2012-05-16
### Added
- First release.
[2.0.1]: https://github.com/nodeca/argparse/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/nodeca/argparse/compare/1.0.10...2.0.0
[1.0.10]: https://github.com/nodeca/argparse/compare/1.0.9...1.0.10
[1.0.9]: https://github.com/nodeca/argparse/compare/1.0.8...1.0.9
[1.0.8]: https://github.com/nodeca/argparse/compare/1.0.7...1.0.8
[1.0.7]: https://github.com/nodeca/argparse/compare/1.0.6...1.0.7
[1.0.6]: https://github.com/nodeca/argparse/compare/1.0.5...1.0.6
[1.0.5]: https://github.com/nodeca/argparse/compare/1.0.4...1.0.5
[1.0.4]: https://github.com/nodeca/argparse/compare/1.0.3...1.0.4
[1.0.3]: https://github.com/nodeca/argparse/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/nodeca/argparse/compare/1.0.1...1.0.2
[1.0.1]: https://github.com/nodeca/argparse/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/nodeca/argparse/compare/0.1.16...1.0.0
[0.1.16]: https://github.com/nodeca/argparse/compare/0.1.15...0.1.16
[0.1.15]: https://github.com/nodeca/argparse/compare/0.1.14...0.1.15
[0.1.14]: https://github.com/nodeca/argparse/compare/0.1.13...0.1.14
[0.1.13]: https://github.com/nodeca/argparse/compare/0.1.12...0.1.13
[0.1.12]: https://github.com/nodeca/argparse/compare/0.1.11...0.1.12
[0.1.11]: https://github.com/nodeca/argparse/compare/0.1.10...0.1.11
[0.1.10]: https://github.com/nodeca/argparse/compare/0.1.9...0.1.10
[0.1.9]: https://github.com/nodeca/argparse/compare/0.1.8...0.1.9
[0.1.8]: https://github.com/nodeca/argparse/compare/0.1.7...0.1.8
[0.1.7]: https://github.com/nodeca/argparse/compare/0.1.6...0.1.7
[0.1.6]: https://github.com/nodeca/argparse/compare/0.1.5...0.1.6
[0.1.5]: https://github.com/nodeca/argparse/compare/0.1.4...0.1.5
[0.1.4]: https://github.com/nodeca/argparse/compare/0.1.3...0.1.4
[0.1.3]: https://github.com/nodeca/argparse/compare/0.1.2...0.1.3
[0.1.2]: https://github.com/nodeca/argparse/compare/0.1.1...0.1.2
[0.1.1]: https://github.com/nodeca/argparse/compare/0.1.0...0.1.1
[0.1.0]: https://github.com/nodeca/argparse/releases/tag/0.1.0

View File

@ -1,254 +0,0 @@
A. HISTORY OF THE SOFTWARE
==========================
Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
as a successor of a language called ABC. Guido remains Python's
principal author, although it includes many contributions from others.
In 1995, Guido continued his work on Python at the Corporation for
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
in Reston, Virginia where he released several versions of the
software.
In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
year, the PythonLabs team moved to Digital Creations, which became
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
https://www.python.org/psf/) was formed, a non-profit organization
created specifically to own Python-related Intellectual Property.
Zope Corporation was a sponsoring member of the PSF.
All Python releases are Open Source (see http://www.opensource.org for
the Open Source Definition). Historically, most, but not all, Python
releases have also been GPL-compatible; the table below summarizes
the various releases.
Release Derived Year Owner GPL-
from compatible? (1)
0.9.0 thru 1.2 1991-1995 CWI yes
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
1.6 1.5.2 2000 CNRI no
2.0 1.6 2000 BeOpen.com no
1.6.1 1.6 2001 CNRI yes (2)
2.1 2.0+1.6.1 2001 PSF no
2.0.1 2.0+1.6.1 2001 PSF yes
2.1.1 2.1+2.0.1 2001 PSF yes
2.1.2 2.1.1 2002 PSF yes
2.1.3 2.1.2 2002 PSF yes
2.2 and above 2.1.1 2001-now PSF yes
Footnotes:
(1) GPL-compatible doesn't mean that we're distributing Python under
the GPL. All Python licenses, unlike the GPL, let you distribute
a modified version without making your changes open source. The
GPL-compatible licenses make it possible to combine Python with
other software that is released under the GPL; the others don't.
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
because its license has a choice of law clause. According to
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
is "not incompatible" with the GPL.
Thanks to the many outside volunteers who have worked under Guido's
direction to make these releases possible.
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
===============================================================
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation;
All Rights Reserved" are retained in Python alone or in any derivative version
prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS"
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions. Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee. This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party. As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee. Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement. This Agreement together with
Python 1.6.1 may be located on the Internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013. This
Agreement may also be obtained from a proxy server on the Internet
using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee. This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.
ACCEPT
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,84 +0,0 @@
argparse
========
[![Build Status](https://secure.travis-ci.org/nodeca/argparse.svg?branch=master)](http://travis-ci.org/nodeca/argparse)
[![NPM version](https://img.shields.io/npm/v/argparse.svg)](https://www.npmjs.org/package/argparse)
CLI arguments parser for node.js, with [sub-commands](https://docs.python.org/3.9/library/argparse.html#sub-commands) support. Port of python's [argparse](http://docs.python.org/dev/library/argparse.html) (version [3.9.0](https://github.com/python/cpython/blob/v3.9.0rc1/Lib/argparse.py)).
**Difference with original.**
- JS has no keyword arguments support.
- Pass options instead: `new ArgumentParser({ description: 'example', add_help: true })`.
- JS has no python's types `int`, `float`, ...
- Use string-typed names: `.add_argument('-b', { type: 'int', help: 'help' })`.
- `%r` format specifier uses `require('util').inspect()`.
More details in [doc](./doc).
Example
-------
`test.js` file:
```javascript
#!/usr/bin/env node
'use strict';
const { ArgumentParser } = require('argparse');
const { version } = require('./package.json');
const parser = new ArgumentParser({
description: 'Argparse example'
});
parser.add_argument('-v', '--version', { action: 'version', version });
parser.add_argument('-f', '--foo', { help: 'foo bar' });
parser.add_argument('-b', '--bar', { help: 'bar foo' });
parser.add_argument('--baz', { help: 'baz bar' });
console.dir(parser.parse_args());
```
Display help:
```
$ ./test.js -h
usage: test.js [-h] [-v] [-f FOO] [-b BAR] [--baz BAZ]
Argparse example
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-f FOO, --foo FOO foo bar
-b BAR, --bar BAR bar foo
--baz BAZ baz bar
```
Parse arguments:
```
$ ./test.js -f=3 --bar=4 --baz 5
{ foo: '3', bar: '4', baz: '5' }
```
API docs
--------
Since this is a port with minimal divergence, there's no separate documentation.
Use original one instead, with notes about difference.
1. [Original doc](https://docs.python.org/3.9/library/argparse.html).
2. [Original tutorial](https://docs.python.org/3.9/howto/argparse.html).
3. [Difference with python](./doc).
argparse for enterprise
-----------------------
Available as part of the Tidelift Subscription
The maintainers of argparse and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-argparse?utm_source=npm-argparse&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

File diff suppressed because it is too large Load Diff

View File

@ -1,67 +0,0 @@
// Limited implementation of python % string operator, supports only %s and %r for now
// (other formats are not used here, but may appear in custom templates)
'use strict'
const { inspect } = require('util')
module.exports = function sub(pattern, ...values) {
let regex = /%(?:(%)|(-)?(\*)?(?:\((\w+)\))?([A-Za-z]))/g
let result = pattern.replace(regex, function (_, is_literal, is_left_align, is_padded, name, format) {
if (is_literal) return '%'
let padded_count = 0
if (is_padded) {
if (values.length === 0) throw new TypeError('not enough arguments for format string')
padded_count = values.shift()
if (!Number.isInteger(padded_count)) throw new TypeError('* wants int')
}
let str
if (name !== undefined) {
let dict = values[0]
if (typeof dict !== 'object' || dict === null) throw new TypeError('format requires a mapping')
if (!(name in dict)) throw new TypeError(`no such key: '${name}'`)
str = dict[name]
} else {
if (values.length === 0) throw new TypeError('not enough arguments for format string')
str = values.shift()
}
switch (format) {
case 's':
str = String(str)
break
case 'r':
str = inspect(str)
break
case 'd':
case 'i':
if (typeof str !== 'number') {
throw new TypeError(`%${format} format: a number is required, not ${typeof str}`)
}
str = String(str.toFixed(0))
break
default:
throw new TypeError(`unsupported format character '${format}'`)
}
if (padded_count > 0) {
return is_left_align ? str.padEnd(padded_count) : str.padStart(padded_count)
} else {
return str
}
})
if (values.length) {
if (values.length === 1 && typeof values[0] === 'object' && values[0] !== null) {
// mapping
} else {
throw new TypeError('not all arguments converted during string formatting')
}
}
return result
}

View File

@ -1,440 +0,0 @@
// Partial port of python's argparse module, version 3.9.0 (only wrap and fill functions):
// https://github.com/python/cpython/blob/v3.9.0b4/Lib/textwrap.py
'use strict'
/*
* Text wrapping and filling.
*/
// Copyright (C) 1999-2001 Gregory P. Ward.
// Copyright (C) 2002, 2003 Python Software Foundation.
// Copyright (C) 2020 argparse.js authors
// Originally written by Greg Ward <gward@python.net>
// Hardcode the recognized whitespace characters to the US-ASCII
// whitespace characters. The main reason for doing this is that
// some Unicode spaces (like \u00a0) are non-breaking whitespaces.
//
// This less funky little regex just split on recognized spaces. E.g.
// "Hello there -- you goof-ball, use the -b option!"
// splits into
// Hello/ /there/ /--/ /you/ /goof-ball,/ /use/ /the/ /-b/ /option!/
const wordsep_simple_re = /([\t\n\x0b\x0c\r ]+)/
class TextWrapper {
/*
* Object for wrapping/filling text. The public interface consists of
* the wrap() and fill() methods; the other methods are just there for
* subclasses to override in order to tweak the default behaviour.
* If you want to completely replace the main wrapping algorithm,
* you'll probably have to override _wrap_chunks().
*
* Several instance attributes control various aspects of wrapping:
* width (default: 70)
* the maximum width of wrapped lines (unless break_long_words
* is false)
* initial_indent (default: "")
* string that will be prepended to the first line of wrapped
* output. Counts towards the line's width.
* subsequent_indent (default: "")
* string that will be prepended to all lines save the first
* of wrapped output; also counts towards each line's width.
* expand_tabs (default: true)
* Expand tabs in input text to spaces before further processing.
* Each tab will become 0 .. 'tabsize' spaces, depending on its position
* in its line. If false, each tab is treated as a single character.
* tabsize (default: 8)
* Expand tabs in input text to 0 .. 'tabsize' spaces, unless
* 'expand_tabs' is false.
* replace_whitespace (default: true)
* Replace all whitespace characters in the input text by spaces
* after tab expansion. Note that if expand_tabs is false and
* replace_whitespace is true, every tab will be converted to a
* single space!
* fix_sentence_endings (default: false)
* Ensure that sentence-ending punctuation is always followed
* by two spaces. Off by default because the algorithm is
* (unavoidably) imperfect.
* break_long_words (default: true)
* Break words longer than 'width'. If false, those words will not
* be broken, and some lines might be longer than 'width'.
* break_on_hyphens (default: true)
* Allow breaking hyphenated words. If true, wrapping will occur
* preferably on whitespaces and right after hyphens part of
* compound words.
* drop_whitespace (default: true)
* Drop leading and trailing whitespace from lines.
* max_lines (default: None)
* Truncate wrapped lines.
* placeholder (default: ' [...]')
* Append to the last line of truncated text.
*/
constructor(options = {}) {
let {
width = 70,
initial_indent = '',
subsequent_indent = '',
expand_tabs = true,
replace_whitespace = true,
fix_sentence_endings = false,
break_long_words = true,
drop_whitespace = true,
break_on_hyphens = true,
tabsize = 8,
max_lines = undefined,
placeholder=' [...]'
} = options
this.width = width
this.initial_indent = initial_indent
this.subsequent_indent = subsequent_indent
this.expand_tabs = expand_tabs
this.replace_whitespace = replace_whitespace
this.fix_sentence_endings = fix_sentence_endings
this.break_long_words = break_long_words
this.drop_whitespace = drop_whitespace
this.break_on_hyphens = break_on_hyphens
this.tabsize = tabsize
this.max_lines = max_lines
this.placeholder = placeholder
}
// -- Private methods -----------------------------------------------
// (possibly useful for subclasses to override)
_munge_whitespace(text) {
/*
* _munge_whitespace(text : string) -> string
*
* Munge whitespace in text: expand tabs and convert all other
* whitespace characters to spaces. Eg. " foo\\tbar\\n\\nbaz"
* becomes " foo bar baz".
*/
if (this.expand_tabs) {
text = text.replace(/\t/g, ' '.repeat(this.tabsize)) // not strictly correct in js
}
if (this.replace_whitespace) {
text = text.replace(/[\t\n\x0b\x0c\r]/g, ' ')
}
return text
}
_split(text) {
/*
* _split(text : string) -> [string]
*
* Split the text to wrap into indivisible chunks. Chunks are
* not quite the same as words; see _wrap_chunks() for full
* details. As an example, the text
* Look, goof-ball -- use the -b option!
* breaks into the following chunks:
* 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ',
* 'use', ' ', 'the', ' ', '-b', ' ', 'option!'
* if break_on_hyphens is True, or in:
* 'Look,', ' ', 'goof-ball', ' ', '--', ' ',
* 'use', ' ', 'the', ' ', '-b', ' ', option!'
* otherwise.
*/
let chunks = text.split(wordsep_simple_re)
chunks = chunks.filter(Boolean)
return chunks
}
_handle_long_word(reversed_chunks, cur_line, cur_len, width) {
/*
* _handle_long_word(chunks : [string],
* cur_line : [string],
* cur_len : int, width : int)
*
* Handle a chunk of text (most likely a word, not whitespace) that
* is too long to fit in any line.
*/
// Figure out when indent is larger than the specified width, and make
// sure at least one character is stripped off on every pass
let space_left
if (width < 1) {
space_left = 1
} else {
space_left = width - cur_len
}
// If we're allowed to break long words, then do so: put as much
// of the next chunk onto the current line as will fit.
if (this.break_long_words) {
cur_line.push(reversed_chunks[reversed_chunks.length - 1].slice(0, space_left))
reversed_chunks[reversed_chunks.length - 1] = reversed_chunks[reversed_chunks.length - 1].slice(space_left)
// Otherwise, we have to preserve the long word intact. Only add
// it to the current line if there's nothing already there --
// that minimizes how much we violate the width constraint.
} else if (!cur_line) {
cur_line.push(...reversed_chunks.pop())
}
// If we're not allowed to break long words, and there's already
// text on the current line, do nothing. Next time through the
// main loop of _wrap_chunks(), we'll wind up here again, but
// cur_len will be zero, so the next line will be entirely
// devoted to the long word that we can't handle right now.
}
_wrap_chunks(chunks) {
/*
* _wrap_chunks(chunks : [string]) -> [string]
*
* Wrap a sequence of text chunks and return a list of lines of
* length 'self.width' or less. (If 'break_long_words' is false,
* some lines may be longer than this.) Chunks correspond roughly
* to words and the whitespace between them: each chunk is
* indivisible (modulo 'break_long_words'), but a line break can
* come between any two chunks. Chunks should not have internal
* whitespace; ie. a chunk is either all whitespace or a "word".
* Whitespace chunks will be removed from the beginning and end of
* lines, but apart from that whitespace is preserved.
*/
let lines = []
let indent
if (this.width <= 0) {
throw Error(`invalid width ${this.width} (must be > 0)`)
}
if (this.max_lines !== undefined) {
if (this.max_lines > 1) {
indent = this.subsequent_indent
} else {
indent = this.initial_indent
}
if (indent.length + this.placeholder.trimStart().length > this.width) {
throw Error('placeholder too large for max width')
}
}
// Arrange in reverse order so items can be efficiently popped
// from a stack of chucks.
chunks = chunks.reverse()
while (chunks.length > 0) {
// Start the list of chunks that will make up the current line.
// cur_len is just the length of all the chunks in cur_line.
let cur_line = []
let cur_len = 0
// Figure out which static string will prefix this line.
let indent
if (lines) {
indent = this.subsequent_indent
} else {
indent = this.initial_indent
}
// Maximum width for this line.
let width = this.width - indent.length
// First chunk on line is whitespace -- drop it, unless this
// is the very beginning of the text (ie. no lines started yet).
if (this.drop_whitespace && chunks[chunks.length - 1].trim() === '' && lines.length > 0) {
chunks.pop()
}
while (chunks.length > 0) {
let l = chunks[chunks.length - 1].length
// Can at least squeeze this chunk onto the current line.
if (cur_len + l <= width) {
cur_line.push(chunks.pop())
cur_len += l
// Nope, this line is full.
} else {
break
}
}
// The current line is full, and the next chunk is too big to
// fit on *any* line (not just this one).
if (chunks.length && chunks[chunks.length - 1].length > width) {
this._handle_long_word(chunks, cur_line, cur_len, width)
cur_len = cur_line.map(l => l.length).reduce((a, b) => a + b, 0)
}
// If the last chunk on this line is all whitespace, drop it.
if (this.drop_whitespace && cur_line.length > 0 && cur_line[cur_line.length - 1].trim() === '') {
cur_len -= cur_line[cur_line.length - 1].length
cur_line.pop()
}
if (cur_line) {
if (this.max_lines === undefined ||
lines.length + 1 < this.max_lines ||
(chunks.length === 0 ||
this.drop_whitespace &&
chunks.length === 1 &&
!chunks[0].trim()) && cur_len <= width) {
// Convert current line back to a string and store it in
// list of all lines (return value).
lines.push(indent + cur_line.join(''))
} else {
let had_break = false
while (cur_line) {
if (cur_line[cur_line.length - 1].trim() &&
cur_len + this.placeholder.length <= width) {
cur_line.push(this.placeholder)
lines.push(indent + cur_line.join(''))
had_break = true
break
}
cur_len -= cur_line[-1].length
cur_line.pop()
}
if (!had_break) {
if (lines) {
let prev_line = lines[lines.length - 1].trimEnd()
if (prev_line.length + this.placeholder.length <=
this.width) {
lines[lines.length - 1] = prev_line + this.placeholder
break
}
}
lines.push(indent + this.placeholder.lstrip())
}
break
}
}
}
return lines
}
_split_chunks(text) {
text = this._munge_whitespace(text)
return this._split(text)
}
// -- Public interface ----------------------------------------------
wrap(text) {
/*
* wrap(text : string) -> [string]
*
* Reformat the single paragraph in 'text' so it fits in lines of
* no more than 'self.width' columns, and return a list of wrapped
* lines. Tabs in 'text' are expanded with string.expandtabs(),
* and all other whitespace characters (including newline) are
* converted to space.
*/
let chunks = this._split_chunks(text)
// not implemented in js
//if (this.fix_sentence_endings) {
// this._fix_sentence_endings(chunks)
//}
return this._wrap_chunks(chunks)
}
fill(text) {
/*
* fill(text : string) -> string
*
* Reformat the single paragraph in 'text' to fit in lines of no
* more than 'self.width' columns, and return a new string
* containing the entire wrapped paragraph.
*/
return this.wrap(text).join('\n')
}
}
// -- Convenience interface ---------------------------------------------
function wrap(text, options = {}) {
/*
* Wrap a single paragraph of text, returning a list of wrapped lines.
*
* Reformat the single paragraph in 'text' so it fits in lines of no
* more than 'width' columns, and return a list of wrapped lines. By
* default, tabs in 'text' are expanded with string.expandtabs(), and
* all other whitespace characters (including newline) are converted to
* space. See TextWrapper class for available keyword args to customize
* wrapping behaviour.
*/
let { width = 70, ...kwargs } = options
let w = new TextWrapper(Object.assign({ width }, kwargs))
return w.wrap(text)
}
function fill(text, options = {}) {
/*
* Fill a single paragraph of text, returning a new string.
*
* Reformat the single paragraph in 'text' to fit in lines of no more
* than 'width' columns, and return a new string containing the entire
* wrapped paragraph. As with wrap(), tabs are expanded and other
* whitespace characters converted to space. See TextWrapper class for
* available keyword args to customize wrapping behaviour.
*/
let { width = 70, ...kwargs } = options
let w = new TextWrapper(Object.assign({ width }, kwargs))
return w.fill(text)
}
// -- Loosely related functionality -------------------------------------
let _whitespace_only_re = /^[ \t]+$/mg
let _leading_whitespace_re = /(^[ \t]*)(?:[^ \t\n])/mg
function dedent(text) {
/*
* Remove any common leading whitespace from every line in `text`.
*
* This can be used to make triple-quoted strings line up with the left
* edge of the display, while still presenting them in the source code
* in indented form.
*
* Note that tabs and spaces are both treated as whitespace, but they
* are not equal: the lines " hello" and "\\thello" are
* considered to have no common leading whitespace.
*
* Entirely blank lines are normalized to a newline character.
*/
// Look for the longest leading string of spaces and tabs common to
// all lines.
let margin = undefined
text = text.replace(_whitespace_only_re, '')
let indents = text.match(_leading_whitespace_re) || []
for (let indent of indents) {
indent = indent.slice(0, -1)
if (margin === undefined) {
margin = indent
// Current line more deeply indented than previous winner:
// no change (previous winner is still on top).
} else if (indent.startsWith(margin)) {
// pass
// Current line consistent with and no deeper than previous winner:
// it's the new winner.
} else if (margin.startsWith(indent)) {
margin = indent
// Find the largest common whitespace between current line and previous
// winner.
} else {
for (let i = 0; i < margin.length && i < indent.length; i++) {
if (margin[i] !== indent[i]) {
margin = margin.slice(0, i)
break
}
}
}
}
if (margin) {
text = text.replace(new RegExp('^' + margin, 'mg'), '')
}
return text
}
module.exports = { wrap, fill, dedent }

View File

@ -1,67 +0,0 @@
{
"_args": [
[
"argparse@2.0.1",
"/home/vitaly/Dropbox/Coding/lv_font_conv"
]
],
"_from": "argparse@2.0.1",
"_id": "argparse@2.0.1",
"_inBundle": false,
"_integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"_location": "/argparse",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "argparse@2.0.1",
"name": "argparse",
"escapedName": "argparse",
"rawSpec": "2.0.1",
"saveSpec": null,
"fetchSpec": "2.0.1"
},
"_requiredBy": [
"/",
"/mocha/js-yaml"
],
"_resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"_spec": "2.0.1",
"_where": "/home/vitaly/Dropbox/Coding/lv_font_conv",
"bugs": {
"url": "https://github.com/nodeca/argparse/issues"
},
"description": "CLI arguments parser. Native port of python's argparse.",
"devDependencies": {
"@babel/eslint-parser": "^7.11.0",
"@babel/plugin-syntax-class-properties": "^7.10.4",
"eslint": "^7.5.0",
"mocha": "^8.0.1",
"nyc": "^15.1.0"
},
"files": [
"argparse.js",
"lib/"
],
"homepage": "https://github.com/nodeca/argparse#readme",
"keywords": [
"cli",
"parser",
"argparse",
"option",
"args"
],
"license": "Python-2.0",
"main": "argparse.js",
"name": "argparse",
"repository": {
"type": "git",
"url": "git+https://github.com/nodeca/argparse.git"
},
"scripts": {
"coverage": "npm run test && nyc report --reporter html",
"lint": "eslint .",
"test": "npm run lint && nyc mocha"
},
"version": "2.0.1"
}

View File

@ -1,36 +0,0 @@
name: Node.js CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x, 14.x, 15.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
lint:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x, 14.x, 15.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run lint

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 bit-buffer developers
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.

View File

@ -1,148 +0,0 @@
# BitBuffer
![Node.js CI](https://github.com/inolen/bit-buffer/workflows/Node.js%20CI/badge.svg)
BitBuffer provides two objects, `BitView` and `BitStream`. `BitView` is a wrapper for ArrayBuffers, similar to JavaScript's [DataView](https://developer.mozilla.org/en-US/docs/JavaScript/Typed_arrays/DataView), but with support for bit-level reads and writes. `BitStream` is a wrapper for a `BitView` used to help maintain your current buffer position, as well as to provide higher-level read / write operations such as for ASCII strings.
## BitView
### Attributes
```javascript
bb.buffer // Underlying ArrayBuffer.
```
```javascript
bb.bigEndian = true; // Switch to big endian (default is little)
```
### Methods
#### BitView(buffer, optional byteOffset, optional byteLength)
Default constructor, takes in a single argument of an ArrayBuffer. Optional are the `byteOffset` and `byteLength` arguments to offset and truncate the view's representation of the buffer.
### getBits(offset, bits, signed)
Reads `bits` number of bits starting at `offset`, twiddling the bits appropriately to return a proper 32-bit signed or unsigned value. NOTE: While JavaScript numbers are 64-bit floating-point values, we don't bother with anything other than the first 32 bits.
### getInt8, getUint8, getInt16, getUint16, getInt32, getUint32(offset)
Shortcuts for getBits, setting the correct `bits` / `signed` values.
### getFloat32(offset)
Gets 32 bits from `offset`, and coerces and returns as a proper float32 value.
### getFloat64(offset)
Gets 64 bits from `offset`, and coerces and returns as a proper float64 value.
### setBits(offset, value, bits)
Sets `bits` number of bits at `offset`.
### setInt8, setUint8, setInt16, setUint16, setInt32, setUint32(offset)
Shortcuts for setBits, setting the correct `bits` count.
### setFloat32(offset)
Coerces a float32 to uint32 and sets at `offset`.
### setFloat64(offset)
Coerces a float64 to two uint32s and sets at `offset`.
## BitStream
### Attributes
```javascript
bb.byteIndex; // Get current index in bytes.
bb.byteIndex = 0; // Set current index in bytes.
```
```javascript
bb.view; // Underlying BitView
```
```javascript
bb.length; // Get the length of the stream in bits
```
```javascript
bb.bitsLeft; // The number of bits left in the stream
```
```javascript
bb.index; // Get the current index in bits
bb.index = 0// Set the current index in bits
```
```javascript
bb.bigEndian = true; // Switch to big endian (default is little)
```
### Methods
#### BitStream(view)
Default constructor, takes in a single argument of a `BitView`, `ArrayBuffer` or node `Buffer`.
#### BitSteam(buffer, optional byteOffset, optional byteLength)
Shortcut constructor that initializes a new `BitView(buffer, byteOffset, byteLength)` for the stream to use.
#### readBits(bits, signed)
Returns `bits` numbers of bits from the view at the current index, updating the index.
#### writeBits(value, bits)
Sets `bits` numbers of bits from `value` in the view at the current index, updating the index.
#### readUint8(), readUint16(), readUint32(), readInt8(), readInt16(), readInt32()
Read a 8, 16 or 32 bits (unsigned) integer at the current index, updating the index.
#### writeUint8(value), writeUint16(value), writeUint32(value), writeInt8(value), writeInt16(value), writeInt32(value)
Write 8, 16 or 32 bits from `value` as (unsigned) integer at the current index, updating the index.
#### readFloat32(), readFloat64()
Read a 32 or 64 bit floating point number at the current index, updating the index.
#### writeFloat32(value), writeFloat64()
Set 32 or 64 bits from `value` as floating point value at the current index, updating the index.
#### readBoolean()
Read a single bit from the view at the current index, updating the index.
#### writeBoolean(value)
Write a single bit to the view at the current index, updating the index.
#### readASCIIString(optional bytes), readUTF8String(optional bytes)
Reads bytes from the underlying view at the current index until either `bytes` count is reached or a 0x00 terminator is reached.
#### writeASCIIString(string, optional bytes), writeUTF8String(string, optional bytes)
Writes a string followed by a NULL character to the underlying view starting at the current index. If the string is longer than `bytes` it will be truncated, and if it is shorter 0x00 will be written in its place.
#### readBitStream(length)
Create a new `BitStream` from the underlying view starting the the current index and a length of `length` bits. Updating the index of the existing `BitStream`
#### readArrayBuffer(byteLength)
Read `byteLength` bytes of data from the underlying view as `ArrayBuffer`, updating the index.
## license
MIT

View File

@ -1,115 +0,0 @@
declare module 'bit-buffer' {
import {Buffer} from 'buffer';
export class BitView {
constructor(buffer: ArrayBuffer | Buffer, byteLength?: number);
readonly buffer: Buffer;
readonly byteLength: number;
bigEndian: boolean;
getBits(offset: number, bits: number, signed?: boolean): number;
getInt8(offset: number): number;
getInt16(offset: number): number;
getInt32(offset: number): number;
getUint8(offset: number): number;
getUint16(offset: number): number;
getUint32(offset: number): number;
getFloat32(offset: number): number;
getFloat64(offset: number): number;
setBits(offset: number, value: number, bits: number);
setInt8(offset: number);
setInt16(offset: number);
setInt32(offset: number);
setUint8(offset: number);
setUint16(offset: number);
setUint32(offset: number);
setFloat32(offset: number, value: number);
setFloat64(offset: number, value: number);
}
export class BitStream {
constructor(source: ArrayBuffer | Buffer | BitView, byteOffset?: number, byteLength?: number)
readonly length: number;
readonly bitsLeft: number;
readonly buffer: Buffer;
readonly view: BitView;
byteIndex: number;
index: number;
bigEndian: boolean;
readBits(bits: number, signed?: boolean): number;
writeBits(value: number, bits: number);
readBoolean(): boolean;
readInt8(): number;
readUint8(): number;
readInt16(): number;
readUint16(): number;
readInt32(): number;
readUint32(): number;
readFloat32(): number;
readFloat64(): number;
writeBoolean(value: boolean);
writeInt8(value: number);
writeUint8(value: number);
writeInt16(value: number);
writeUint16(value: number);
writeInt32(value: number);
writeUint32(value: number);
writeFloat32(value: number);
writeFloat64(value: number);
readASCIIString(length?: number): string;
readUTF8String(length?: number): string;
writeASCIIString(data: string, length?: number);
writeUTF8String(data: string, length?: number);
readBitStream(length: number): BitStream;
readArrayBuffer(byteLength: number): Uint8Array;
writeBitStream(stream: BitStream, length?: number);
writeArrayBuffer(buffer: BitStream, length?: number);
}
}

View File

@ -1,502 +0,0 @@
(function (root) {
/**********************************************************
*
* BitView
*
* BitView provides a similar interface to the standard
* DataView, but with support for bit-level reads / writes.
*
**********************************************************/
var BitView = function (source, byteOffset, byteLength) {
var isBuffer = source instanceof ArrayBuffer ||
(typeof Buffer !== 'undefined' && source instanceof Buffer);
if (!isBuffer) {
throw new Error('Must specify a valid ArrayBuffer or Buffer.');
}
byteOffset = byteOffset || 0;
byteLength = byteLength || source.byteLength /* ArrayBuffer */ || source.length /* Buffer */;
this._view = new Uint8Array(source.buffer || source, byteOffset, byteLength);
this.bigEndian = false;
};
// Used to massage fp values so we can operate on them
// at the bit level.
BitView._scratch = new DataView(new ArrayBuffer(8));
Object.defineProperty(BitView.prototype, 'buffer', {
get: function () { return typeof Buffer !== 'undefined' ? Buffer.from(this._view.buffer) : this._view.buffer; },
enumerable: true,
configurable: false
});
Object.defineProperty(BitView.prototype, 'byteLength', {
get: function () { return this._view.length; },
enumerable: true,
configurable: false
});
BitView.prototype._setBit = function (offset, on) {
if (on) {
this._view[offset >> 3] |= 1 << (offset & 7);
} else {
this._view[offset >> 3] &= ~(1 << (offset & 7));
}
};
BitView.prototype.getBits = function (offset, bits, signed) {
var available = (this._view.length * 8 - offset);
if (bits > available) {
throw new Error('Cannot get ' + bits + ' bit(s) from offset ' + offset + ', ' + available + ' available');
}
var value = 0;
for (var i = 0; i < bits;) {
var remaining = bits - i;
var bitOffset = offset & 7;
var currentByte = this._view[offset >> 3];
// the max number of bits we can read from the current byte
var read = Math.min(remaining, 8 - bitOffset);
var mask, readBits;
if (this.bigEndian) {
// create a mask with the correct bit width
mask = ~(0xFF << read);
// shift the bits we want to the start of the byte and mask of the rest
readBits = (currentByte >> (8 - read - bitOffset)) & mask;
value <<= read;
value |= readBits;
} else {
// create a mask with the correct bit width
mask = ~(0xFF << read);
// shift the bits we want to the start of the byte and mask off the rest
readBits = (currentByte >> bitOffset) & mask;
value |= readBits << i;
}
offset += read;
i += read;
}
if (signed) {
// If we're not working with a full 32 bits, check the
// imaginary MSB for this bit count and convert to a
// valid 32-bit signed value if set.
if (bits !== 32 && value & (1 << (bits - 1))) {
value |= -1 ^ ((1 << bits) - 1);
}
return value;
}
return value >>> 0;
};
BitView.prototype.setBits = function (offset, value, bits) {
var available = (this._view.length * 8 - offset);
if (bits > available) {
throw new Error('Cannot set ' + bits + ' bit(s) from offset ' + offset + ', ' + available + ' available');
}
for (var i = 0; i < bits;) {
var remaining = bits - i;
var bitOffset = offset & 7;
var byteOffset = offset >> 3;
var wrote = Math.min(remaining, 8 - bitOffset);
var mask, writeBits, destMask;
if (this.bigEndian) {
// create a mask with the correct bit width
mask = ~(~0 << wrote);
// shift the bits we want to the start of the byte and mask of the rest
writeBits = (value >> (bits - i - wrote)) & mask;
var destShift = 8 - bitOffset - wrote;
// destination mask to zero all the bits we're changing first
destMask = ~(mask << destShift);
this._view[byteOffset] =
(this._view[byteOffset] & destMask)
| (writeBits << destShift);
} else {
// create a mask with the correct bit width
mask = ~(0xFF << wrote);
// shift the bits we want to the start of the byte and mask of the rest
writeBits = value & mask;
value >>= wrote;
// destination mask to zero all the bits we're changing first
destMask = ~(mask << bitOffset);
this._view[byteOffset] =
(this._view[byteOffset] & destMask)
| (writeBits << bitOffset);
}
offset += wrote;
i += wrote;
}
};
BitView.prototype.getBoolean = function (offset) {
return this.getBits(offset, 1, false) !== 0;
};
BitView.prototype.getInt8 = function (offset) {
return this.getBits(offset, 8, true);
};
BitView.prototype.getUint8 = function (offset) {
return this.getBits(offset, 8, false);
};
BitView.prototype.getInt16 = function (offset) {
return this.getBits(offset, 16, true);
};
BitView.prototype.getUint16 = function (offset) {
return this.getBits(offset, 16, false);
};
BitView.prototype.getInt32 = function (offset) {
return this.getBits(offset, 32, true);
};
BitView.prototype.getUint32 = function (offset) {
return this.getBits(offset, 32, false);
};
BitView.prototype.getFloat32 = function (offset) {
BitView._scratch.setUint32(0, this.getUint32(offset));
return BitView._scratch.getFloat32(0);
};
BitView.prototype.getFloat64 = function (offset) {
BitView._scratch.setUint32(0, this.getUint32(offset));
// DataView offset is in bytes.
BitView._scratch.setUint32(4, this.getUint32(offset+32));
return BitView._scratch.getFloat64(0);
};
BitView.prototype.setBoolean = function (offset, value) {
this.setBits(offset, value ? 1 : 0, 1);
};
BitView.prototype.setInt8 =
BitView.prototype.setUint8 = function (offset, value) {
this.setBits(offset, value, 8);
};
BitView.prototype.setInt16 =
BitView.prototype.setUint16 = function (offset, value) {
this.setBits(offset, value, 16);
};
BitView.prototype.setInt32 =
BitView.prototype.setUint32 = function (offset, value) {
this.setBits(offset, value, 32);
};
BitView.prototype.setFloat32 = function (offset, value) {
BitView._scratch.setFloat32(0, value);
this.setBits(offset, BitView._scratch.getUint32(0), 32);
};
BitView.prototype.setFloat64 = function (offset, value) {
BitView._scratch.setFloat64(0, value);
this.setBits(offset, BitView._scratch.getUint32(0), 32);
this.setBits(offset+32, BitView._scratch.getUint32(4), 32);
};
BitView.prototype.getArrayBuffer = function (offset, byteLength) {
var buffer = new Uint8Array(byteLength);
for (var i = 0; i < byteLength; i++) {
buffer[i] = this.getUint8(offset + (i * 8));
}
return buffer;
};
/**********************************************************
*
* BitStream
*
* Small wrapper for a BitView to maintain your position,
* as well as to handle reading / writing of string data
* to the underlying buffer.
*
**********************************************************/
var reader = function (name, size) {
return function () {
if (this._index + size > this._length) {
throw new Error('Trying to read past the end of the stream');
}
var val = this._view[name](this._index);
this._index += size;
return val;
};
};
var writer = function (name, size) {
return function (value) {
this._view[name](this._index, value);
this._index += size;
};
};
function readASCIIString(stream, bytes) {
return readString(stream, bytes, false);
}
function readUTF8String(stream, bytes) {
return readString(stream, bytes, true);
}
function readString(stream, bytes, utf8) {
if (bytes === 0) {
return '';
}
var i = 0;
var chars = [];
var append = true;
var fixedLength = !!bytes;
if (!bytes) {
bytes = Math.floor((stream._length - stream._index) / 8);
}
// Read while we still have space available, or until we've
// hit the fixed byte length passed in.
while (i < bytes) {
var c = stream.readUint8();
// Stop appending chars once we hit 0x00
if (c === 0x00) {
append = false;
// If we don't have a fixed length to read, break out now.
if (!fixedLength) {
break;
}
}
if (append) {
chars.push(c);
}
i++;
}
var string = String.fromCharCode.apply(null, chars);
if (utf8) {
try {
return decodeURIComponent(escape(string)); // https://stackoverflow.com/a/17192845
} catch (e) {
return string;
}
} else {
return string;
}
}
function writeASCIIString(stream, string, bytes) {
var length = bytes || string.length + 1; // + 1 for NULL
for (var i = 0; i < length; i++) {
stream.writeUint8(i < string.length ? string.charCodeAt(i) : 0x00);
}
}
function writeUTF8String(stream, string, bytes) {
var byteArray = stringToByteArray(string);
var length = bytes || byteArray.length + 1; // + 1 for NULL
for (var i = 0; i < length; i++) {
stream.writeUint8(i < byteArray.length ? byteArray[i] : 0x00);
}
}
function stringToByteArray(str) { // https://gist.github.com/volodymyr-mykhailyk/2923227
var b = [], i, unicode;
for (i = 0; i < str.length; i++) {
unicode = str.charCodeAt(i);
// 0x00000000 - 0x0000007f -> 0xxxxxxx
if (unicode <= 0x7f) {
b.push(unicode);
// 0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
} else if (unicode <= 0x7ff) {
b.push((unicode >> 6) | 0xc0);
b.push((unicode & 0x3F) | 0x80);
// 0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
} else if (unicode <= 0xffff) {
b.push((unicode >> 12) | 0xe0);
b.push(((unicode >> 6) & 0x3f) | 0x80);
b.push((unicode & 0x3f) | 0x80);
// 0x00010000 - 0x001fffff -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
} else {
b.push((unicode >> 18) | 0xf0);
b.push(((unicode >> 12) & 0x3f) | 0x80);
b.push(((unicode >> 6) & 0x3f) | 0x80);
b.push((unicode & 0x3f) | 0x80);
}
}
return b;
}
var BitStream = function (source, byteOffset, byteLength) {
var isBuffer = source instanceof ArrayBuffer ||
(typeof Buffer !== 'undefined' && source instanceof Buffer);
if (!(source instanceof BitView) && !isBuffer) {
throw new Error('Must specify a valid BitView, ArrayBuffer or Buffer');
}
if (isBuffer) {
this._view = new BitView(source, byteOffset, byteLength);
} else {
this._view = source;
}
this._index = 0;
this._startIndex = 0;
this._length = this._view.byteLength * 8;
};
Object.defineProperty(BitStream.prototype, 'index', {
get: function () { return this._index - this._startIndex; },
set: function (val) { this._index = val + this._startIndex; },
enumerable: true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'length', {
get: function () { return this._length - this._startIndex; },
set: function (val) { this._length = val + this._startIndex; },
enumerable : true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'bitsLeft', {
get: function () { return this._length - this._index; },
enumerable : true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'byteIndex', {
// Ceil the returned value, over compensating for the amount of
// bits written to the stream.
get: function () { return Math.ceil(this._index / 8); },
set: function (val) { this._index = val * 8; },
enumerable: true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'buffer', {
get: function () { return this._view.buffer; },
enumerable: true,
configurable: false
});
Object.defineProperty(BitStream.prototype, 'view', {
get: function () { return this._view; },
enumerable: true,
configurable: false
});
Object.defineProperty(BitStream.prototype, 'bigEndian', {
get: function () { return this._view.bigEndian; },
set: function (val) { this._view.bigEndian = val; },
enumerable: true,
configurable: false
});
BitStream.prototype.readBits = function (bits, signed) {
var val = this._view.getBits(this._index, bits, signed);
this._index += bits;
return val;
};
BitStream.prototype.writeBits = function (value, bits) {
this._view.setBits(this._index, value, bits);
this._index += bits;
};
BitStream.prototype.readBoolean = reader('getBoolean', 1);
BitStream.prototype.readInt8 = reader('getInt8', 8);
BitStream.prototype.readUint8 = reader('getUint8', 8);
BitStream.prototype.readInt16 = reader('getInt16', 16);
BitStream.prototype.readUint16 = reader('getUint16', 16);
BitStream.prototype.readInt32 = reader('getInt32', 32);
BitStream.prototype.readUint32 = reader('getUint32', 32);
BitStream.prototype.readFloat32 = reader('getFloat32', 32);
BitStream.prototype.readFloat64 = reader('getFloat64', 64);
BitStream.prototype.writeBoolean = writer('setBoolean', 1);
BitStream.prototype.writeInt8 = writer('setInt8', 8);
BitStream.prototype.writeUint8 = writer('setUint8', 8);
BitStream.prototype.writeInt16 = writer('setInt16', 16);
BitStream.prototype.writeUint16 = writer('setUint16', 16);
BitStream.prototype.writeInt32 = writer('setInt32', 32);
BitStream.prototype.writeUint32 = writer('setUint32', 32);
BitStream.prototype.writeFloat32 = writer('setFloat32', 32);
BitStream.prototype.writeFloat64 = writer('setFloat64', 64);
BitStream.prototype.readASCIIString = function (bytes) {
return readASCIIString(this, bytes);
};
BitStream.prototype.readUTF8String = function (bytes) {
return readUTF8String(this, bytes);
};
BitStream.prototype.writeASCIIString = function (string, bytes) {
writeASCIIString(this, string, bytes);
};
BitStream.prototype.writeUTF8String = function (string, bytes) {
writeUTF8String(this, string, bytes);
};
BitStream.prototype.readBitStream = function(bitLength) {
var slice = new BitStream(this._view);
slice._startIndex = this._index;
slice._index = this._index;
slice.length = bitLength;
this._index += bitLength;
return slice;
};
BitStream.prototype.writeBitStream = function(stream, length) {
if (!length) {
length = stream.bitsLeft;
}
var bitsToWrite;
while (length > 0) {
bitsToWrite = Math.min(length, 32);
this.writeBits(stream.readBits(bitsToWrite), bitsToWrite);
length -= bitsToWrite;
}
};
BitStream.prototype.readArrayBuffer = function(byteLength) {
var buffer = this._view.getArrayBuffer(this._index, byteLength);
this._index += (byteLength * 8);
return buffer;
};
BitStream.prototype.writeArrayBuffer = function(buffer, byteLength) {
this.writeBitStream(new BitStream(buffer), byteLength * 8);
};
// AMD / RequireJS
if (typeof define !== 'undefined' && define.amd) {
define(function () {
return {
BitView: BitView,
BitStream: BitStream
};
});
}
// Node.js
else if (typeof module !== 'undefined' && module.exports) {
module.exports = {
BitView: BitView,
BitStream: BitStream
};
}
}(this));

View File

@ -1,71 +0,0 @@
{
"_args": [
[
"bit-buffer@0.2.5",
"/home/vitaly/Dropbox/Coding/lv_font_conv"
]
],
"_from": "bit-buffer@0.2.5",
"_id": "bit-buffer@0.2.5",
"_inBundle": false,
"_integrity": "sha512-x1yGnmXvFg6e3DiyRztElbcn1bsCTFSoM/ncAzY62uE0JdTl5xlKJd0ooqLYoPbhdsnpehSIQrdIvclcZJYwiA==",
"_location": "/bit-buffer",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "bit-buffer@0.2.5",
"name": "bit-buffer",
"escapedName": "bit-buffer",
"rawSpec": "0.2.5",
"saveSpec": null,
"fetchSpec": "0.2.5"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/bit-buffer/-/bit-buffer-0.2.5.tgz",
"_spec": "0.2.5",
"_where": "/home/vitaly/Dropbox/Coding/lv_font_conv",
"author": {
"name": "Anthony Pesch"
},
"bugs": {
"url": "https://github.com/inolen/bit-buffer/issues"
},
"contributors": [
{
"name": "Robin Appelman"
}
],
"description": "Bit-level reads and writes for ArrayBuffers",
"devDependencies": {
"@types/node": "^14.14.22",
"jshint": "^2.12.0",
"mocha": "^8.2.1"
},
"directories": {
"test": "test"
},
"gitHead": "cd4417237bed1f22dd5adfd8a6b961ea7234d9c9",
"homepage": "https://github.com/inolen/bit-buffer#readme",
"keywords": [
"dataview",
"arraybuffer",
"bit",
"bits"
],
"license": "MIT",
"main": "bit-buffer.js",
"name": "bit-buffer",
"repository": {
"type": "git",
"url": "git://github.com/inolen/bit-buffer.git"
},
"scripts": {
"lint": "jshint bit-buffer.js",
"test": "mocha --ui tdd"
},
"types": "./bit-buffer.d.ts",
"version": "0.2.5"
}

View File

@ -1,628 +0,0 @@
var assert = require('assert'),
BitView = require('./bit-buffer').BitView,
BitStream = require('./bit-buffer').BitStream;
suite('BitBuffer', function () {
var array, bv, bsw, bsr;
setup(function () {
array = new ArrayBuffer(64);
bv = new BitView(array);
bsw = new BitStream(bv);
// Test initializing straight from the array.
bsr = new BitStream(array);
});
test('Min / max signed 5 bits', function () {
var signed_max = (1 << 4) - 1;
bsw.writeBits(signed_max, 5);
bsw.writeBits(-signed_max - 1, 5);
assert(bsr.readBits(5, true) === signed_max);
assert(bsr.readBits(5, true) === -signed_max - 1);
});
test('Min / max unsigned 5 bits', function () {
var unsigned_max = (1 << 5) - 1;
bsw.writeBits(unsigned_max, 5);
bsw.writeBits(-unsigned_max, 5);
assert.equal(bsr.readBits(5), unsigned_max);
assert.equal(bsr.readBits(5), 1);
});
test('Min / max int8', function () {
var signed_max = 0x7F;
bsw.writeInt8(signed_max);
bsw.writeInt8(-signed_max - 1);
assert.equal(bsr.readInt8(), signed_max);
assert.equal(bsr.readInt8(), -signed_max - 1);
});
test('Min / max uint8', function () {
var unsigned_max = 0xFF;
bsw.writeUint8(unsigned_max);
bsw.writeUint8(-unsigned_max);
assert.equal(bsr.readUint8(), unsigned_max);
assert.equal(bsr.readUint8(), 1);
});
test('Min / max int16', function () {
var signed_max = 0x7FFF;
bsw.writeInt16(signed_max);
bsw.writeInt16(-signed_max - 1);
assert.equal(bsr.readInt16(), signed_max);
assert.equal(bsr.readInt16(), -signed_max - 1);
});
test('Min / max uint16', function () {
var unsigned_max = 0xFFFF;
bsw.writeUint16(unsigned_max);
bsw.writeUint16(-unsigned_max);
assert.equal(bsr.readUint16(), unsigned_max);
assert.equal(bsr.readUint16(), 1);
});
test('Min / max int32', function () {
var signed_max = 0x7FFFFFFF;
bsw.writeInt32(signed_max);
bsw.writeInt32(-signed_max - 1);
assert.equal(bsr.readInt32(), signed_max);
assert.equal(bsr.readInt32(), -signed_max - 1);
});
test('Min / max uint32', function () {
var unsigned_max = 0xFFFFFFFF;
bsw.writeUint32(unsigned_max);
bsw.writeUint32(-unsigned_max);
assert.equal(bsr.readUint32(), unsigned_max);
assert.equal(bsr.readUint32(), 1);
});
test('Unaligned reads', function () {
bsw.writeBits(13, 5);
bsw.writeUint8(0xFF);
bsw.writeBits(14, 5);
assert.equal(bsr.readBits(5), 13);
assert.equal(bsr.readUint8(), 0xFF);
assert.equal(bsr.readBits(5), 14);
});
test('Min / max float32 (normal values)', function () {
var scratch = new DataView(new ArrayBuffer(8));
scratch.setUint32(0, 0x00800000);
scratch.setUint32(4, 0x7f7fffff);
var min = scratch.getFloat32(0);
var max = scratch.getFloat32(4);
bsw.writeFloat32(min);
bsw.writeFloat32(max);
assert.equal(bsr.readFloat32(), min);
assert.equal(bsr.readFloat32(), max);
});
test('Min / max float64 (normal values)', function () {
var scratch = new DataView(new ArrayBuffer(16));
scratch.setUint32(0, 0x00100000);
scratch.setUint32(4, 0x00000000);
scratch.setUint32(8, 0x7fefffff);
scratch.setUint32(12, 0xffffffff);
var min = scratch.getFloat64(0);
var max = scratch.getFloat64(8);
bsw.writeFloat64(min);
bsw.writeFloat64(max);
assert.equal(bsr.readFloat64(), min);
assert.equal(bsr.readFloat64(), max);
});
test('Overwrite previous value with 0', function () {
bv.setUint8(0, 13);
bv.setUint8(0, 0);
assert.equal(bv.getUint8(0), 0);
});
test('Read / write ASCII string, fixed length', function () {
var str = 'foobar';
var len = 16;
bsw.writeASCIIString(str, len);
assert.equal(bsw.byteIndex, len);
assert.equal(bsr.readASCIIString(len), str);
assert.equal(bsr.byteIndex, len);
});
test('Read / write ASCII string, unknown length', function () {
var str = 'foobar';
bsw.writeASCIIString(str);
assert.equal(bsw.byteIndex, str.length + 1); // +1 for 0x00
assert.equal(bsr.readASCIIString(), str);
assert.equal(bsr.byteIndex, str.length + 1);
});
test('Read ASCII string, 0 length', function () {
var str = 'foobar';
bsw.writeASCIIString(str);
assert.equal(bsw.byteIndex, str.length + 1); // +1 for 0x00
assert.equal(bsr.readASCIIString(0), '');
assert.equal(bsr.byteIndex, 0);
});
test('Read overflow', function () {
var exception = false;
try {
bsr.readASCIIString(128);
} catch (e) {
exception = true;
}
assert(exception);
});
test('Write overflow', function () {
var exception = false;
try {
bsw.writeASCIIString('foobar', 128);
} catch (e) {
exception = true;
}
assert(exception);
});
test('Get boolean', function () {
bv.setUint8(0, 1);
assert(bv.getBoolean(0));
bv.setUint8(0, 0);
assert(!bv.getBoolean(0));
});
test('Set boolean', function () {
bv.setBoolean(0, true);
assert(bv.getBoolean(0));
bv.setBoolean(0, false);
assert(!bv.getBoolean(0));
});
test('Read boolean', function () {
bv.setBits(0, 1, 1);
bv.setBits(1, 0, 1);
assert(bsr.readBoolean());
assert(!bsr.readBoolean());
});
test('Write boolean', function () {
bsr.writeBoolean(true);
assert.equal(bv.getBits(0, 1, false), 1);
bsr.writeBoolean(false);
assert.equal(bv.getBits(1, 1, false), 0);
});
test('Read / write UTF8 string, only ASCII characters', function () {
var str = 'foobar';
bsw.writeUTF8String(str);
assert(bsw.byteIndex === str.length + 1); // +1 for 0x00
assert.equal(bsr.readUTF8String(), str);
assert.equal(bsr.byteIndex, str.length + 1);
});
test('Read / write UTF8 string, non ASCII characters', function () {
var str = '日本語';
var bytes = [
0xE6,
0x97,
0xA5,
0xE6,
0x9C,
0xAC,
0xE8,
0xAA,
0x9E
];
bsw.writeUTF8String(str);
for (var i = 0; i < bytes.length; i++) {
assert.equal(bytes[i], bv.getBits(i * 8, 8));
}
assert.equal(bsw.byteIndex, bytes.length + 1); // +1 for 0x00
assert.equal(str, bsr.readUTF8String());
assert.equal(bsr.byteIndex, bytes.length + 1);
});
test('readBitStream', function () {
bsw.writeBits(0xF0, 8); //0b11110000
bsw.writeBits(0xF1, 8); //0b11110001
bsr.readBits(3); //offset
var slice = bsr.readBitStream(8);
assert.equal(slice.readBits(6), 0x3E); //0b111110
assert.equal(9, slice._index);
assert.equal(6, slice.index);
assert.equal(8, slice.length);
assert.equal(2, slice.bitsLeft);
assert.equal(bsr._index, 11);
assert.equal((64 * 8) - 11, bsr.bitsLeft);
});
test('readBitStream overflow', function () {
bsw.writeBits(0xF0, 8); //0b11110000
bsw.writeBits(0xF1, 8); //0b11110001
bsr.readBits(3); //offset
var slice = bsr.readBitStream(4);
var exception = false;
try {
slice.readUint8();
} catch (e) {
exception = true;
}
assert(exception);
});
test('writeBitStream', function () {
var buf = new ArrayBuffer(64);
var sourceStream = new BitStream(buf);
sourceStream.writeBits(0xF0, 8); //0b11110000
sourceStream.writeBits(0xF1, 8); //0b11110001
sourceStream.index = 0;
sourceStream.readBits(3); //offset
bsr.writeBitStream(sourceStream, 8);
assert.equal(8, bsr.index);
bsr.index = 0;
assert.equal(bsr.readBits(6), 0x3E); //0b00111110
assert.equal(11, sourceStream.index);
var bin = new Uint8Array(buf);
assert.equal(bin[0], 0xF0);
assert.equal(bin[1], 0xF1);
});
test('writeBitStream Buffer', function () {
var buf = Buffer.alloc(64);
var sourceStream = new BitStream(buf);
sourceStream.writeBits(0xF0, 8); //0b11110000
sourceStream.writeBits(0xF1, 8); //0b11110001
sourceStream.index = 0;
sourceStream.readBits(3); //offset
bsr.writeBitStream(sourceStream, 8);
assert.equal(8, bsr.index);
bsr.index = 0;
assert.equal(bsr.readBits(6), 0x3E); //0b00111110
assert.equal(11, sourceStream.index);
var bin = new Uint8Array(buf.buffer);
assert.equal(bin[0], 0xF0);
assert.equal(bin[1], 0xF1);
});
test('writeBitStream long', function () {
var sourceStream = new BitStream(new ArrayBuffer(64));
sourceStream.writeBits(0xF0, 8);
sourceStream.writeBits(0xF1, 8);
sourceStream.writeBits(0xF1, 8);
sourceStream.writeBits(0xF1, 8);
sourceStream.writeBits(0xF1, 8);
sourceStream.index = 0;
sourceStream.readBits(3); //offset
bsr.index = 3;
bsr.writeBitStream(sourceStream, 35);
assert.equal(38, bsr.index);
bsr.index = 3;
assert.equal(bsr.readBits(35), 1044266558);
assert.equal(38, sourceStream.index);
});
test('readArrayBuffer', function () {
bsw.writeBits(0xF0, 8); //0b11110000
bsw.writeBits(0xF1, 8); //0b11110001
bsw.writeBits(0xF0, 8); //0b11110000
bsr.readBits(3); //offset
var buffer = bsr.readArrayBuffer(2);
assert.equal(0x3E, buffer[0]); //0b00111110
assert.equal(0x1E, buffer[1]); //0b00011110
assert.equal(3 + (2 * 8), bsr._index);
});
test('writeArrayBuffer', function () {
var source = new Uint8Array(4);
source[0] = 0xF0;
source[1] = 0xF1;
source[2] = 0xF1;
bsr.readBits(3); //offset
bsr.writeArrayBuffer(source.buffer, 2);
assert.equal(19, bsr.index);
bsr.index = 0;
assert.equal(bsr.readBits(8), 128);
});
test('Get buffer from view', function () {
bv.setBits(0, 0xFFFFFFFF, 32);
var buffer = bv.buffer;
assert.equal(64, buffer.length);
assert.equal(0xFFFF, buffer.readUInt16LE(0));
});
test('Get buffer from stream', function () {
bsw.writeBits(0xFFFFFFFF, 32);
var buffer = bsr.buffer;
assert.equal(64, buffer.length);
assert.equal(0xFFFF, buffer.readUInt16LE(0));
});
});
suite('Reading big/little endian', function () {
var array, u8, bv, bsw, bsr;
setup(function () {
array = new ArrayBuffer(64);
u8 = new Uint8Array(array);
u8[0] = 0x01;
u8[1] = 0x02;
// Test initializing straight from the array.
bsr = new BitStream(array);
});
test('4b, little-endian', function () {
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(4));
result.push(bsr.readBits(4));
result.push(bsr.readBits(4));
result.push(bsr.readBits(4));
// 0000 0001 0000 0010 [01 02]
// [#2] [#1] [#4] [#3]
assert.deepEqual(result, [1, 0, 2, 0]);
});
test('8b, little-endian', function () {
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(8));
result.push(bsr.readBits(8));
// 0000 0001 0000 0010 [01 02]
// [ #1] [ #2]
assert.deepEqual(result, [1, 2]);
});
test('10b, little-endian', function () {
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(10));
// 0000 0001 0000 0010 [01 02]
// ... #1] [ #2][#1...
assert.deepEqual(result, [513]);
});
test('16b, little-endian', function () {
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(16));
// 0000 0001 0000 0010 [01 02]
// [ #1]
assert.deepEqual(result, [0x201]);
});
test('24b, little-endian', function () {
u8[2] = 0x03;
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(24));
// 0000 0001 0000 0010 0000 0011 [01 02 03]
// [ #1]
assert.deepEqual(result, [0x30201]);
});
test('4b, big-endian', function () {
bsr.bigEndian = true;
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(4));
result.push(bsr.readBits(4));
result.push(bsr.readBits(4));
result.push(bsr.readBits(4));
// 0000 0001 0000 0010 [01 02]
// [#1] [#2] [#3] [#4]
assert.deepEqual(result, [0, 1, 0, 2]);
});
test('8b, big-endian', function () {
bsr.bigEndian = true;
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(8));
result.push(bsr.readBits(8));
// 0000 0001 0000 0010 [01 02]
// [ #1] [ #2]
assert.deepEqual(result, [1, 2]);
});
test('10b, big-endian', function () {
bsr.bigEndian = true;
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(10));
result.push(bsr.readBits(6));
// 0000 0001 0000 0010 [01 02]
// [ #1][ #2]
assert.deepEqual(result, [4, 2]);
});
test('16b, big-endian', function () {
bsr.bigEndian = true;
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(16));
// 0000 0001 0000 0010 [01 02]
// [ #1]
assert.deepEqual(result, [0x102]);
});
test('24b, big-endian', function () {
u8[2] = 0x03;
bsr.bigEndian = true;
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
var result = [];
result.push(bsr.readBits(24));
// 0000 0001 0000 0010 0000 0011 [01 02 03]
// [ #1]
assert.deepEqual(result, [0x10203]);
});
});
suite('Writing big/little endian', function () {
var array, u8, bv, bsw, bsr;
setup(function () {
array = new ArrayBuffer(2);
u8 = new Uint8Array(array);
bv = new BitView(array);
bsw = new BitStream(bv);
});
test('4b, little-endian', function () {
// 0000 0001 0000 0010 [01 02]
// [#2] [#1] [#4] [#3]
bsw.writeBits(1, 4);
bsw.writeBits(0, 4);
bsw.writeBits(2, 4);
bsw.writeBits(0, 4);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
test('8b, little-endian', function () {
// 0000 0001 0000 0010 [01 02]
// [ #1] [ #2]
bsw.writeBits(1, 8);
bsw.writeBits(2, 8);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
test('10b, little-endian', function () {
// 0000 0001 0000 0010 [01 02]
// ... #1] [ #2][#1...
bsw.writeBits(513, 10);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
test('16b, little-endian', function () {
// 0000 0001 0000 0010 [01 02]
// [ #1]
bsw.writeBits(0x201, 16);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
test('4b, big-endian', function () {
bsw.bigEndian = true;
// 0000 0001 0000 0010 [01 02]
// [#1] [#2] [#3] [#4]
bsw.writeBits(0, 4);
bsw.writeBits(1, 4);
bsw.writeBits(0, 4);
bsw.writeBits(2, 4);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
test('8b, big-endian', function () {
bsw.bigEndian = true;
// 0000 0001 0000 0010 [01 02]
// [ #1] [ #2]
bsw.writeBits(1, 8);
bsw.writeBits(2, 8);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
test('10b, big-endian', function () {
bsw.bigEndian = true;
// 0000 0001 0000 0010 [01 02]
// [ #1][ #2]
bsw.writeBits(4, 10);
bsw.writeBits(2, 6);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
test('16b, big-endian', function () {
bsw.bigEndian = true;
// 0000 0001 0000 0010 [01 02]
// [ #1]
bsw.writeBits(0x102, 16);
assert.deepEqual(u8, new Uint8Array([0x01, 0x02]));
});
});

View File

@ -1,19 +0,0 @@
(The MIT License)
Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
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.

View File

@ -1,455 +0,0 @@
# debug
[![Build Status](https://travis-ci.org/visionmedia/debug.svg?branch=master)](https://travis-ci.org/visionmedia/debug) [![Coverage Status](https://coveralls.io/repos/github/visionmedia/debug/badge.svg?branch=master)](https://coveralls.io/github/visionmedia/debug?branch=master) [![Slack](https://visionmedia-community-slackin.now.sh/badge.svg)](https://visionmedia-community-slackin.now.sh/) [![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/debug/sponsors/badge.svg)](#sponsors)
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
A tiny JavaScript debugging utility modelled after Node.js core's debugging
technique. Works in Node.js and web browsers.
## Installation
```bash
$ npm install debug
```
## Usage
`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
Example [_app.js_](./examples/node/app.js):
```js
var debug = require('debug')('http')
, http = require('http')
, name = 'My App';
// fake app
debug('booting %o', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
res.end('hello\n');
}).listen(3000, function(){
debug('listening');
});
// fake worker of some kind
require('./worker');
```
Example [_worker.js_](./examples/node/worker.js):
```js
var a = require('debug')('worker:a')
, b = require('debug')('worker:b');
function work() {
a('doing lots of uninteresting work');
setTimeout(work, Math.random() * 1000);
}
work();
function workb() {
b('doing some work');
setTimeout(workb, Math.random() * 2000);
}
workb();
```
The `DEBUG` environment variable is then used to enable these based on space or
comma-delimited names.
Here are some examples:
<img width="647" alt="screen shot 2017-08-08 at 12 53 04 pm" src="https://user-images.githubusercontent.com/71256/29091703-a6302cdc-7c38-11e7-8304-7c0b3bc600cd.png">
<img width="647" alt="screen shot 2017-08-08 at 12 53 38 pm" src="https://user-images.githubusercontent.com/71256/29091700-a62a6888-7c38-11e7-800b-db911291ca2b.png">
<img width="647" alt="screen shot 2017-08-08 at 12 53 25 pm" src="https://user-images.githubusercontent.com/71256/29091701-a62ea114-7c38-11e7-826a-2692bedca740.png">
#### Windows command prompt notes
##### CMD
On Windows the environment variable is set using the `set` command.
```cmd
set DEBUG=*,-not_this
```
Example:
```cmd
set DEBUG=* & node app.js
```
##### PowerShell (VS Code default)
PowerShell uses different syntax to set environment variables.
```cmd
$env:DEBUG = "*,-not_this"
```
Example:
```cmd
$env:DEBUG='app';node app.js
```
Then, run the program to be debugged as usual.
npm script example:
```js
"windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js",
```
## Namespace Colors
Every debug instance has a color generated for it based on its namespace name.
This helps when visually parsing the debug output to identify which debug instance
a debug line belongs to.
#### Node.js
In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
otherwise debug will only use a small handful of basic colors.
<img width="521" src="https://user-images.githubusercontent.com/71256/29092181-47f6a9e6-7c3a-11e7-9a14-1928d8a711cd.png">
#### Web Browser
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).
<img width="524" src="https://user-images.githubusercontent.com/71256/29092033-b65f9f2e-7c39-11e7-8e32-f6f0d8e865c1.png">
## Millisecond diff
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
<img width="647" src="https://user-images.githubusercontent.com/71256/29091956-6bd78372-7c39-11e7-8c55-c948396d6edd.png">
## Conventions
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.
## Wildcards
The `*` character may be used as a wildcard. Suppose for example your library has
debuggers named "connect:bodyParser", "connect:compress", "connect:session",
instead of listing all three with
`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
You can also exclude specific debuggers by prefixing them with a "-" character.
For example, `DEBUG=*,-connect:*` would include all debuggers except those
starting with "connect:".
## Environment Variables
When running through Node.js, you can set a few environment variables that will
change the behavior of the debug logging:
| Name | Purpose |
|-----------|-------------------------------------------------|
| `DEBUG` | Enables/disables specific debugging namespaces. |
| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
| `DEBUG_DEPTH` | Object inspection depth. |
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
__Note:__ The environment variables beginning with `DEBUG_` end up being
converted into an Options object that gets used with `%o`/`%O` formatters.
See the Node.js documentation for
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
for the complete list.
## Formatters
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
Below are the officially supported formatters:
| Formatter | Representation |
|-----------|----------------|
| `%O` | Pretty-print an Object on multiple lines. |
| `%o` | Pretty-print an Object all on a single line. |
| `%s` | String. |
| `%d` | Number (both integer and float). |
| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
| `%%` | Single percent sign ('%'). This does not consume an argument. |
### Custom formatters
You can add custom formatters by extending the `debug.formatters` object.
For example, if you wanted to add support for rendering a Buffer as hex with
`%h`, you could do something like:
```js
const createDebug = require('debug')
createDebug.formatters.h = (v) => {
return v.toString('hex')
}
// …elsewhere
const debug = createDebug('foo')
debug('this is hex: %h', new Buffer('hello world'))
// foo this is hex: 68656c6c6f20776f726c6421 +0ms
```
## Browser Support
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
if you don't want to build it yourself.
Debug's enable state is currently persisted by `localStorage`.
Consider the situation shown below where you have `worker:a` and `worker:b`,
and wish to debug both. You can enable this using `localStorage.debug`:
```js
localStorage.debug = 'worker:*'
```
And then refresh the page.
```js
a = debug('worker:a');
b = debug('worker:b');
setInterval(function(){
a('doing some work');
}, 1000);
setInterval(function(){
b('doing some work');
}, 1200);
```
## Output streams
By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
Example [_stdout.js_](./examples/node/stdout.js):
```js
var debug = require('debug');
var error = debug('app:error');
// by default stderr is used
error('goes to stderr!');
var log = debug('app:log');
// set this namespace to log via console.log
log.log = console.log.bind(console); // don't forget to bind to console!
log('goes to stdout');
error('still goes to stderr!');
// set all output to go via console.info
// overrides all per-namespace log settings
debug.log = console.info.bind(console);
error('now goes to stdout via console.info');
log('still goes to stdout, but via console.info now');
```
## Extend
You can simply extend debugger
```js
const log = require('debug')('auth');
//creates new debug instance with extended namespace
const logSign = log.extend('sign');
const logLogin = log.extend('login');
log('hello'); // auth hello
logSign('hello'); //auth:sign hello
logLogin('hello'); //auth:login hello
```
## Set dynamically
You can also enable debug dynamically by calling the `enable()` method :
```js
let debug = require('debug');
console.log(1, debug.enabled('test'));
debug.enable('test');
console.log(2, debug.enabled('test'));
debug.disable();
console.log(3, debug.enabled('test'));
```
print :
```
1 false
2 true
3 false
```
Usage :
`enable(namespaces)`
`namespaces` can include modes separated by a colon and wildcards.
Note that calling `enable()` completely overrides previously set DEBUG variable :
```
$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))'
=> false
```
`disable()`
Will disable all namespaces. The functions returns the namespaces currently
enabled (and skipped). This can be useful if you want to disable debugging
temporarily without knowing what was enabled to begin with.
For example:
```js
let debug = require('debug');
debug.enable('foo:*,-foo:bar');
let namespaces = debug.disable();
debug.enable(namespaces);
```
Note: There is no guarantee that the string will be identical to the initial
enable string, but semantically they will be identical.
## Checking whether a debug target is enabled
After you've created a debug instance, you can determine whether or not it is
enabled by checking the `enabled` property:
```javascript
const debug = require('debug')('http');
if (debug.enabled) {
// do stuff...
}
```
You can also manually toggle this property to force the debug instance to be
enabled or disabled.
## Authors
- TJ Holowaychuk
- Nathan Rajlich
- Andrew Rhyne
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
<a href="https://opencollective.com/debug/backer/0/website" target="_blank"><img src="https://opencollective.com/debug/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/1/website" target="_blank"><img src="https://opencollective.com/debug/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/2/website" target="_blank"><img src="https://opencollective.com/debug/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/3/website" target="_blank"><img src="https://opencollective.com/debug/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/4/website" target="_blank"><img src="https://opencollective.com/debug/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/5/website" target="_blank"><img src="https://opencollective.com/debug/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/6/website" target="_blank"><img src="https://opencollective.com/debug/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/7/website" target="_blank"><img src="https://opencollective.com/debug/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/8/website" target="_blank"><img src="https://opencollective.com/debug/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/9/website" target="_blank"><img src="https://opencollective.com/debug/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/10/website" target="_blank"><img src="https://opencollective.com/debug/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/11/website" target="_blank"><img src="https://opencollective.com/debug/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/12/website" target="_blank"><img src="https://opencollective.com/debug/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/13/website" target="_blank"><img src="https://opencollective.com/debug/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/14/website" target="_blank"><img src="https://opencollective.com/debug/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/15/website" target="_blank"><img src="https://opencollective.com/debug/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/16/website" target="_blank"><img src="https://opencollective.com/debug/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/17/website" target="_blank"><img src="https://opencollective.com/debug/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/18/website" target="_blank"><img src="https://opencollective.com/debug/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/19/website" target="_blank"><img src="https://opencollective.com/debug/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/20/website" target="_blank"><img src="https://opencollective.com/debug/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/21/website" target="_blank"><img src="https://opencollective.com/debug/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/22/website" target="_blank"><img src="https://opencollective.com/debug/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/23/website" target="_blank"><img src="https://opencollective.com/debug/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/24/website" target="_blank"><img src="https://opencollective.com/debug/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/25/website" target="_blank"><img src="https://opencollective.com/debug/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/26/website" target="_blank"><img src="https://opencollective.com/debug/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/27/website" target="_blank"><img src="https://opencollective.com/debug/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/28/website" target="_blank"><img src="https://opencollective.com/debug/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/29/website" target="_blank"><img src="https://opencollective.com/debug/backer/29/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
<a href="https://opencollective.com/debug/sponsor/0/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/1/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/2/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/3/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/4/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/5/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/6/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/7/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/8/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/9/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/10/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/11/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/12/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/13/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/14/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/15/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/16/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/17/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/18/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/19/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/20/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/21/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/22/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/23/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/24/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/25/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/26/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/27/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/28/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/29/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/29/avatar.svg"></a>
## License
(The MIT License)
Copyright (c) 2014-2017 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
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.

View File

@ -1,111 +0,0 @@
{
"_args": [
[
"debug@4.3.1",
"/home/vitaly/Dropbox/Coding/lv_font_conv"
]
],
"_from": "debug@4.3.1",
"_id": "debug@4.3.1",
"_inBundle": false,
"_integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"_location": "/debug",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "debug@4.3.1",
"name": "debug",
"escapedName": "debug",
"rawSpec": "4.3.1",
"saveSpec": null,
"fetchSpec": "4.3.1"
},
"_requiredBy": [
"/",
"/@babel/core",
"/@babel/helper-define-polyfill-provider",
"/@babel/traverse",
"/@eslint/eslintrc",
"/eslint",
"/istanbul-lib-source-maps",
"/mocha"
],
"_resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"_spec": "4.3.1",
"_where": "/home/vitaly/Dropbox/Coding/lv_font_conv",
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
"browser": "./src/browser.js",
"bugs": {
"url": "https://github.com/visionmedia/debug/issues"
},
"contributors": [
{
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
"url": "http://n8.io"
},
{
"name": "Andrew Rhyne",
"email": "rhyneandrew@gmail.com"
},
{
"name": "Josh Junon",
"email": "josh@junon.me"
}
],
"dependencies": {
"ms": "2.1.2"
},
"description": "small debugging utility",
"devDependencies": {
"brfs": "^2.0.1",
"browserify": "^16.2.3",
"coveralls": "^3.0.2",
"istanbul": "^0.4.5",
"karma": "^3.1.4",
"karma-browserify": "^6.0.0",
"karma-chrome-launcher": "^2.2.0",
"karma-mocha": "^1.3.0",
"mocha": "^5.2.0",
"mocha-lcov-reporter": "^1.2.0",
"xo": "^0.23.0"
},
"engines": {
"node": ">=6.0"
},
"files": [
"src",
"LICENSE",
"README.md"
],
"homepage": "https://github.com/visionmedia/debug#readme",
"keywords": [
"debug",
"log",
"debugger"
],
"license": "MIT",
"main": "./src/index.js",
"name": "debug",
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
},
"repository": {
"type": "git",
"url": "git://github.com/visionmedia/debug.git"
},
"scripts": {
"lint": "xo",
"test": "npm run test:node && npm run test:browser && npm run lint",
"test:browser": "karma start --single-run",
"test:coverage": "cat ./coverage/lcov.info | coveralls",
"test:node": "istanbul cover _mocha -- test.js"
},
"version": "4.3.1"
}

View File

@ -1,269 +0,0 @@
/* eslint-env browser */
/**
* This is the web browser implementation of `debug()`.
*/
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = localstorage();
exports.destroy = (() => {
let warned = false;
return () => {
if (!warned) {
warned = true;
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
}
};
})();
/**
* Colors.
*/
exports.colors = [
'#0000CC',
'#0000FF',
'#0033CC',
'#0033FF',
'#0066CC',
'#0066FF',
'#0099CC',
'#0099FF',
'#00CC00',
'#00CC33',
'#00CC66',
'#00CC99',
'#00CCCC',
'#00CCFF',
'#3300CC',
'#3300FF',
'#3333CC',
'#3333FF',
'#3366CC',
'#3366FF',
'#3399CC',
'#3399FF',
'#33CC00',
'#33CC33',
'#33CC66',
'#33CC99',
'#33CCCC',
'#33CCFF',
'#6600CC',
'#6600FF',
'#6633CC',
'#6633FF',
'#66CC00',
'#66CC33',
'#9900CC',
'#9900FF',
'#9933CC',
'#9933FF',
'#99CC00',
'#99CC33',
'#CC0000',
'#CC0033',
'#CC0066',
'#CC0099',
'#CC00CC',
'#CC00FF',
'#CC3300',
'#CC3333',
'#CC3366',
'#CC3399',
'#CC33CC',
'#CC33FF',
'#CC6600',
'#CC6633',
'#CC9900',
'#CC9933',
'#CCCC00',
'#CCCC33',
'#FF0000',
'#FF0033',
'#FF0066',
'#FF0099',
'#FF00CC',
'#FF00FF',
'#FF3300',
'#FF3333',
'#FF3366',
'#FF3399',
'#FF33CC',
'#FF33FF',
'#FF6600',
'#FF6633',
'#FF9900',
'#FF9933',
'#FFCC00',
'#FFCC33'
];
/**
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
* and the Firebug extension (any Firefox version) are known
* to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
// eslint-disable-next-line complexity
function useColors() {
// NB: In an Electron preload script, document will be defined but not fully
// initialized. Since we know we're in Chrome, we'll just detect this case
// explicitly
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
return true;
}
// Internet Explorer and Edge do not support colors.
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
return false;
}
// Is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
// Is firebug? http://stackoverflow.com/a/398120/376773
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
// Is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
// Double check webkit in userAgent just in case we are in a worker
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs(args) {
args[0] = (this.useColors ? '%c' : '') +
this.namespace +
(this.useColors ? ' %c' : ' ') +
args[0] +
(this.useColors ? '%c ' : ' ') +
'+' + module.exports.humanize(this.diff);
if (!this.useColors) {
return;
}
const c = 'color: ' + this.color;
args.splice(1, 0, c, 'color: inherit');
// The final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
let index = 0;
let lastC = 0;
args[0].replace(/%[a-zA-Z%]/g, match => {
if (match === '%%') {
return;
}
index++;
if (match === '%c') {
// We only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
}
/**
* Invokes `console.debug()` when available.
* No-op when `console.debug` is not a "function".
* If `console.debug` is not available, falls back
* to `console.log`.
*
* @api public
*/
exports.log = console.debug || console.log || (() => {});
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (namespaces) {
exports.storage.setItem('debug', namespaces);
} else {
exports.storage.removeItem('debug');
}
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
let r;
try {
r = exports.storage.getItem('debug');
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
if (!r && typeof process !== 'undefined' && 'env' in process) {
r = process.env.DEBUG;
}
return r;
}
/**
* Localstorage attempts to return the localstorage.
*
* This is necessary because safari throws
* when a user disables cookies/localstorage
* and you attempt to access it.
*
* @return {LocalStorage}
* @api private
*/
function localstorage() {
try {
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
// The Browser also has localStorage in the global context.
return localStorage;
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
}
module.exports = require('./common')(exports);
const {formatters} = module.exports;
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
formatters.j = function (v) {
try {
return JSON.stringify(v);
} catch (error) {
return '[UnexpectedJSONParseError]: ' + error.message;
}
};

View File

@ -1,261 +0,0 @@
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*/
function setup(env) {
createDebug.debug = createDebug;
createDebug.default = createDebug;
createDebug.coerce = coerce;
createDebug.disable = disable;
createDebug.enable = enable;
createDebug.enabled = enabled;
createDebug.humanize = require('ms');
createDebug.destroy = destroy;
Object.keys(env).forEach(key => {
createDebug[key] = env[key];
});
/**
* The currently active debug mode names, and names to skip.
*/
createDebug.names = [];
createDebug.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
*/
createDebug.formatters = {};
/**
* Selects a color for a debug namespace
* @param {String} namespace The namespace string for the for the debug instance to be colored
* @return {Number|String} An ANSI color code for the given namespace
* @api private
*/
function selectColor(namespace) {
let hash = 0;
for (let i = 0; i < namespace.length; i++) {
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
}
createDebug.selectColor = selectColor;
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function createDebug(namespace) {
let prevTime;
let enableOverride = null;
function debug(...args) {
// Disabled?
if (!debug.enabled) {
return;
}
const self = debug;
// Set `diff` timestamp
const curr = Number(new Date());
const ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
args[0] = createDebug.coerce(args[0]);
if (typeof args[0] !== 'string') {
// Anything else let's inspect with %O
args.unshift('%O');
}
// Apply any `formatters` transformations
let index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
// If we encounter an escaped % then don't increase the array index
if (match === '%%') {
return '%';
}
index++;
const formatter = createDebug.formatters[format];
if (typeof formatter === 'function') {
const val = args[index];
match = formatter.call(self, val);
// Now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
// Apply env-specific formatting (colors, etc.)
createDebug.formatArgs.call(self, args);
const logFn = self.log || createDebug.log;
logFn.apply(self, args);
}
debug.namespace = namespace;
debug.useColors = createDebug.useColors();
debug.color = createDebug.selectColor(namespace);
debug.extend = extend;
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
Object.defineProperty(debug, 'enabled', {
enumerable: true,
configurable: false,
get: () => enableOverride === null ? createDebug.enabled(namespace) : enableOverride,
set: v => {
enableOverride = v;
}
});
// Env-specific initialization logic for debug instances
if (typeof createDebug.init === 'function') {
createDebug.init(debug);
}
return debug;
}
function extend(namespace, delimiter) {
const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
newDebug.log = this.log;
return newDebug;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
createDebug.save(namespaces);
createDebug.names = [];
createDebug.skips = [];
let i;
const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
const len = split.length;
for (i = 0; i < len; i++) {
if (!split[i]) {
// ignore empty strings
continue;
}
namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else {
createDebug.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @return {String} namespaces
* @api public
*/
function disable() {
const namespaces = [
...createDebug.names.map(toNamespace),
...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
].join(',');
createDebug.enable('');
return namespaces;
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
if (name[name.length - 1] === '*') {
return true;
}
let i;
let len;
for (i = 0, len = createDebug.skips.length; i < len; i++) {
if (createDebug.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = createDebug.names.length; i < len; i++) {
if (createDebug.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Convert regexp to namespace
*
* @param {RegExp} regxep
* @return {String} namespace
* @api private
*/
function toNamespace(regexp) {
return regexp.toString()
.substring(2, regexp.toString().length - 2)
.replace(/\.\*\?$/, '*');
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) {
return val.stack || val.message;
}
return val;
}
/**
* XXX DO NOT USE. This is a temporary stub function.
* XXX It WILL be removed in the next major release.
*/
function destroy() {
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
}
createDebug.enable(createDebug.load());
return createDebug;
}
module.exports = setup;

View File

@ -1,10 +0,0 @@
/**
* Detect Electron renderer / nwjs process, which is node, but we should
* treat as a browser.
*/
if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
module.exports = require('./browser.js');
} else {
module.exports = require('./node.js');
}

View File

@ -1,263 +0,0 @@
/**
* Module dependencies.
*/
const tty = require('tty');
const util = require('util');
/**
* This is the Node.js implementation of `debug()`.
*/
exports.init = init;
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.destroy = util.deprecate(
() => {},
'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'
);
/**
* Colors.
*/
exports.colors = [6, 2, 3, 4, 5, 1];
try {
// Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json)
// eslint-disable-next-line import/no-extraneous-dependencies
const supportsColor = require('supports-color');
if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
exports.colors = [
20,
21,
26,
27,
32,
33,
38,
39,
40,
41,
42,
43,
44,
45,
56,
57,
62,
63,
68,
69,
74,
75,
76,
77,
78,
79,
80,
81,
92,
93,
98,
99,
112,
113,
128,
129,
134,
135,
148,
149,
160,
161,
162,
163,
164,
165,
166,
167,
168,
169,
170,
171,
172,
173,
178,
179,
184,
185,
196,
197,
198,
199,
200,
201,
202,
203,
204,
205,
206,
207,
208,
209,
214,
215,
220,
221
];
}
} catch (error) {
// Swallow - we only care if `supports-color` is available; it doesn't have to be.
}
/**
* Build up the default `inspectOpts` object from the environment variables.
*
* $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
*/
exports.inspectOpts = Object.keys(process.env).filter(key => {
return /^debug_/i.test(key);
}).reduce((obj, key) => {
// Camel-case
const prop = key
.substring(6)
.toLowerCase()
.replace(/_([a-z])/g, (_, k) => {
return k.toUpperCase();
});
// Coerce string value into JS value
let val = process.env[key];
if (/^(yes|on|true|enabled)$/i.test(val)) {
val = true;
} else if (/^(no|off|false|disabled)$/i.test(val)) {
val = false;
} else if (val === 'null') {
val = null;
} else {
val = Number(val);
}
obj[prop] = val;
return obj;
}, {});
/**
* Is stdout a TTY? Colored output is enabled when `true`.
*/
function useColors() {
return 'colors' in exports.inspectOpts ?
Boolean(exports.inspectOpts.colors) :
tty.isatty(process.stderr.fd);
}
/**
* Adds ANSI color escape codes if enabled.
*
* @api public
*/
function formatArgs(args) {
const {namespace: name, useColors} = this;
if (useColors) {
const c = this.color;
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
const prefix = ` ${colorCode};1m${name} \u001B[0m`;
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
} else {
args[0] = getDate() + name + ' ' + args[0];
}
}
function getDate() {
if (exports.inspectOpts.hideDate) {
return '';
}
return new Date().toISOString() + ' ';
}
/**
* Invokes `util.format()` with the specified arguments and writes to stderr.
*/
function log(...args) {
return process.stderr.write(util.format(...args) + '\n');
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
if (namespaces) {
process.env.DEBUG = namespaces;
} else {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete process.env.DEBUG;
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
return process.env.DEBUG;
}
/**
* Init logic for `debug` instances.
*
* Create a new `inspectOpts` object in case `useColors` is set
* differently for a particular `debug` instance.
*/
function init(debug) {
debug.inspectOpts = {};
const keys = Object.keys(exports.inspectOpts);
for (let i = 0; i < keys.length; i++) {
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
}
}
module.exports = require('./common')(exports);
const {formatters} = module.exports;
/**
* Map %o to `util.inspect()`, all on a single line.
*/
formatters.o = function (v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts)
.split('\n')
.map(str => str.trim())
.join(' ');
};
/**
* Map %O to `util.inspect()`, allowing multiple lines if needed.
*/
formatters.O = function (v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts);
};

View File

@ -1,5 +0,0 @@
Copyright 2014 Julien Fontanet
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,112 +0,0 @@
# make-error
[![Package Version](https://badgen.net/npm/v/make-error)](https://npmjs.org/package/make-error) [![Build Status](https://travis-ci.org/JsCommunity/make-error.png?branch=master)](https://travis-ci.org/JsCommunity/make-error) [![PackagePhobia](https://badgen.net/packagephobia/install/make-error)](https://packagephobia.now.sh/result?p=make-error) [![Latest Commit](https://badgen.net/github/last-commit/JsCommunity/make-error)](https://github.com/JsCommunity/make-error/commits/master)
> Make your own error types!
## Features
- Compatible Node & browsers
- `instanceof` support
- `error.name` & `error.stack` support
- compatible with [CSP](https://en.wikipedia.org/wiki/Content_Security_Policy) (i.e. no `eval()`)
## Installation
### Node & [Browserify](http://browserify.org/)/[Webpack](https://webpack.js.org/)
Installation of the [npm package](https://npmjs.org/package/make-error):
```
> npm install --save make-error
```
Then require the package:
```javascript
var makeError = require("make-error");
```
### Browser
You can directly use the build provided at [unpkg.com](https://unpkg.com):
```html
<script src="https://unpkg.com/make-error@1/dist/make-error.js"></script>
```
## Usage
### Basic named error
```javascript
var CustomError = makeError("CustomError");
// Parameters are forwarded to the super class (here Error).
throw new CustomError("a message");
```
### Advanced error class
```javascript
function CustomError(customValue) {
CustomError.super.call(this, "custom error message");
this.customValue = customValue;
}
makeError(CustomError);
// Feel free to extend the prototype.
CustomError.prototype.myMethod = function CustomError$myMethod() {
console.log("CustomError.myMethod (%s, %s)", this.code, this.message);
};
//-----
try {
throw new CustomError(42);
} catch (error) {
error.myMethod();
}
```
### Specialized error
```javascript
var SpecializedError = makeError("SpecializedError", CustomError);
throw new SpecializedError(42);
```
### Inheritance
> Best for ES2015+.
```javascript
import { BaseError } from "make-error";
class CustomError extends BaseError {
constructor() {
super("custom error message");
}
}
```
## Related
- [make-error-cause](https://www.npmjs.com/package/make-error-cause): Make your own error types, with a cause!
## Contributions
Contributions are _very_ welcomed, either on the documentation or on
the code.
You may:
- report any [issue](https://github.com/JsCommunity/make-error/issues)
you've encountered;
- fork and create a pull request.
## License
ISC © [Julien Fontanet](http://julien.isonoe.net)

View File

@ -1 +0,0 @@
!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).makeError=f()}}(function(){return function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){return o(e[i][1][r]||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}({1:[function(require,module,exports){"use strict";var construct="undefined"!=typeof Reflect?Reflect.construct:void 0,defineProperty=Object.defineProperty,captureStackTrace=Error.captureStackTrace;function BaseError(message){void 0!==message&&defineProperty(this,"message",{configurable:!0,value:message,writable:!0});var cname=this.constructor.name;void 0!==cname&&cname!==this.name&&defineProperty(this,"name",{configurable:!0,value:cname,writable:!0}),captureStackTrace(this,this.constructor)}void 0===captureStackTrace&&(captureStackTrace=function(error){var container=new Error;defineProperty(error,"stack",{configurable:!0,get:function(){var stack=container.stack;return defineProperty(this,"stack",{configurable:!0,value:stack,writable:!0}),stack},set:function(stack){defineProperty(error,"stack",{configurable:!0,value:stack,writable:!0})}})}),BaseError.prototype=Object.create(Error.prototype,{constructor:{configurable:!0,value:BaseError,writable:!0}});var setFunctionName=function(){function setFunctionName(fn,name){return defineProperty(fn,"name",{configurable:!0,value:name})}try{var f=function(){};if(setFunctionName(f,"foo"),"foo"===f.name)return setFunctionName}catch(_){}}();(module.exports=function(constructor,super_){if(null==super_||super_===Error)super_=BaseError;else if("function"!=typeof super_)throw new TypeError("super_ should be a function");var name;if("string"==typeof constructor)name=constructor,constructor=void 0!==construct?function(){return construct(super_,arguments,this.constructor)}:function(){super_.apply(this,arguments)},void 0!==setFunctionName&&(setFunctionName(constructor,name),name=void 0);else if("function"!=typeof constructor)throw new TypeError("constructor should be either a string or a function");constructor.super_=constructor.super=super_;var properties={constructor:{configurable:!0,value:constructor,writable:!0}};return void 0!==name&&(properties.name={configurable:!0,value:name,writable:!0}),constructor.prototype=Object.create(super_.prototype,properties),constructor}).BaseError=BaseError},{}]},{},[1])(1)});

View File

@ -1,47 +0,0 @@
/**
* Create a new error constructor instance.
*/
declare function makeError(
name: string
): makeError.Constructor<makeError.BaseError>;
/**
* Set the constructor prototype to `BaseError`.
*/
declare function makeError<T extends Error>(super_: {
new (...args: any[]): T;
}): makeError.Constructor<T & makeError.BaseError>;
/**
* Create a specialized error instance.
*/
declare function makeError<T extends Error, K>(
name: string | Function,
super_: K
): K & makeError.SpecializedConstructor<T>;
declare namespace makeError {
/**
* Use with ES2015+ inheritance.
*/
export class BaseError extends Error {
message: string;
name: string;
stack: string;
constructor(message?: string);
}
export interface Constructor<T> {
new (message?: string): T;
super_: any;
prototype: T;
}
export interface SpecializedConstructor<T> {
super_: any;
prototype: T;
}
}
export = makeError;

View File

@ -1,151 +0,0 @@
// ISC @ Julien Fontanet
"use strict";
// ===================================================================
var construct = typeof Reflect !== "undefined" ? Reflect.construct : undefined;
var defineProperty = Object.defineProperty;
// -------------------------------------------------------------------
var captureStackTrace = Error.captureStackTrace;
if (captureStackTrace === undefined) {
captureStackTrace = function captureStackTrace(error) {
var container = new Error();
defineProperty(error, "stack", {
configurable: true,
get: function getStack() {
var stack = container.stack;
// Replace property with value for faster future accesses.
defineProperty(this, "stack", {
configurable: true,
value: stack,
writable: true,
});
return stack;
},
set: function setStack(stack) {
defineProperty(error, "stack", {
configurable: true,
value: stack,
writable: true,
});
},
});
};
}
// -------------------------------------------------------------------
function BaseError(message) {
if (message !== undefined) {
defineProperty(this, "message", {
configurable: true,
value: message,
writable: true,
});
}
var cname = this.constructor.name;
if (cname !== undefined && cname !== this.name) {
defineProperty(this, "name", {
configurable: true,
value: cname,
writable: true,
});
}
captureStackTrace(this, this.constructor);
}
BaseError.prototype = Object.create(Error.prototype, {
// See: https://github.com/JsCommunity/make-error/issues/4
constructor: {
configurable: true,
value: BaseError,
writable: true,
},
});
// -------------------------------------------------------------------
// Sets the name of a function if possible (depends of the JS engine).
var setFunctionName = (function() {
function setFunctionName(fn, name) {
return defineProperty(fn, "name", {
configurable: true,
value: name,
});
}
try {
var f = function() {};
setFunctionName(f, "foo");
if (f.name === "foo") {
return setFunctionName;
}
} catch (_) {}
})();
// -------------------------------------------------------------------
function makeError(constructor, super_) {
if (super_ == null || super_ === Error) {
super_ = BaseError;
} else if (typeof super_ !== "function") {
throw new TypeError("super_ should be a function");
}
var name;
if (typeof constructor === "string") {
name = constructor;
constructor =
construct !== undefined
? function() {
return construct(super_, arguments, this.constructor);
}
: function() {
super_.apply(this, arguments);
};
// If the name can be set, do it once and for all.
if (setFunctionName !== undefined) {
setFunctionName(constructor, name);
name = undefined;
}
} else if (typeof constructor !== "function") {
throw new TypeError("constructor should be either a string or a function");
}
// Also register the super constructor also as `constructor.super_` just
// like Node's `util.inherits()`.
//
// eslint-disable-next-line dot-notation
constructor.super_ = constructor["super"] = super_;
var properties = {
constructor: {
configurable: true,
value: constructor,
writable: true,
},
};
// If the name could not be set on the constructor, set it on the
// prototype.
if (name !== undefined) {
properties.name = {
configurable: true,
value: name,
writable: true,
};
}
constructor.prototype = Object.create(super_.prototype, properties);
return constructor;
}
exports = module.exports = makeError;
exports.BaseError = BaseError;

View File

@ -1,95 +0,0 @@
{
"_args": [
[
"make-error@1.3.6",
"/home/vitaly/Dropbox/Coding/lv_font_conv"
]
],
"_from": "make-error@1.3.6",
"_id": "make-error@1.3.6",
"_inBundle": false,
"_integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"_location": "/make-error",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "make-error@1.3.6",
"name": "make-error",
"escapedName": "make-error",
"rawSpec": "1.3.6",
"saveSpec": null,
"fetchSpec": "1.3.6"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"_spec": "1.3.6",
"_where": "/home/vitaly/Dropbox/Coding/lv_font_conv",
"author": {
"name": "Julien Fontanet",
"email": "julien.fontanet@isonoe.net"
},
"bugs": {
"url": "https://github.com/JsCommunity/make-error/issues"
},
"description": "Make your own error types!",
"devDependencies": {
"browserify": "^16.2.3",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.4.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"husky": "^3.0.9",
"jest": "^24",
"prettier": "^1.14.3",
"uglify-js": "^3.3.2"
},
"files": [
"dist/",
"index.js",
"index.d.ts"
],
"homepage": "https://github.com/JsCommunity/make-error",
"husky": {
"hooks": {
"commit-msg": "npm run test"
}
},
"jest": {
"testEnvironment": "node"
},
"keywords": [
"create",
"custom",
"derive",
"error",
"errors",
"extend",
"extending",
"extension",
"factory",
"inherit",
"make",
"subclass"
],
"license": "ISC",
"main": "index.js",
"name": "make-error",
"repository": {
"type": "git",
"url": "git://github.com/JsCommunity/make-error.git"
},
"scripts": {
"dev-test": "jest --watch",
"format": "prettier --write '**'",
"prepublishOnly": "mkdir -p dist && browserify -s makeError index.js | uglifyjs -c > dist/make-error.js",
"pretest": "eslint --ignore-path .gitignore .",
"test": "jest"
},
"version": "1.3.6"
}

View File

@ -1,15 +0,0 @@
# Changers Lorgs!
## 1.0
Full rewrite. Essentially a brand new module.
- Return a promise instead of taking a callback.
- Use native `fs.mkdir(path, { recursive: true })` when available.
- Drop support for outdated Node.js versions. (Technically still works on
Node.js v8, but only 10 and above are officially supported.)
## 0.x
Original and most widely used recursive directory creation implementation
in JavaScript, dating back to 2010.

View File

@ -1,21 +0,0 @@
Copyright James Halliday (mail@substack.net) and Isaac Z. Schlueter (i@izs.me)
This project is free software released under the MIT license:
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.

View File

@ -1,68 +0,0 @@
#!/usr/bin/env node
const usage = () => `
usage: mkdirp [DIR1,DIR2..] {OPTIONS}
Create each supplied directory including any necessary parent directories
that don't yet exist.
If the directory already exists, do nothing.
OPTIONS are:
-m<mode> If a directory needs to be created, set the mode as an octal
--mode=<mode> permission string.
-v --version Print the mkdirp version number
-h --help Print this helpful banner
-p --print Print the first directories created for each path provided
--manual Use manual implementation, even if native is available
`
const dirs = []
const opts = {}
let print = false
let dashdash = false
let manual = false
for (const arg of process.argv.slice(2)) {
if (dashdash)
dirs.push(arg)
else if (arg === '--')
dashdash = true
else if (arg === '--manual')
manual = true
else if (/^-h/.test(arg) || /^--help/.test(arg)) {
console.log(usage())
process.exit(0)
} else if (arg === '-v' || arg === '--version') {
console.log(require('../package.json').version)
process.exit(0)
} else if (arg === '-p' || arg === '--print') {
print = true
} else if (/^-m/.test(arg) || /^--mode=/.test(arg)) {
const mode = parseInt(arg.replace(/^(-m|--mode=)/, ''), 8)
if (isNaN(mode)) {
console.error(`invalid mode argument: ${arg}\nMust be an octal number.`)
process.exit(1)
}
opts.mode = mode
} else
dirs.push(arg)
}
const mkdirp = require('../')
const impl = manual ? mkdirp.manual : mkdirp
if (dirs.length === 0)
console.error(usage())
Promise.all(dirs.map(dir => impl(dir, opts)))
.then(made => print ? made.forEach(m => m && console.log(m)) : null)
.catch(er => {
console.error(er.message)
if (er.code)
console.error(' code: ' + er.code)
process.exit(1)
})

View File

@ -1,31 +0,0 @@
const optsArg = require('./lib/opts-arg.js')
const pathArg = require('./lib/path-arg.js')
const {mkdirpNative, mkdirpNativeSync} = require('./lib/mkdirp-native.js')
const {mkdirpManual, mkdirpManualSync} = require('./lib/mkdirp-manual.js')
const {useNative, useNativeSync} = require('./lib/use-native.js')
const mkdirp = (path, opts) => {
path = pathArg(path)
opts = optsArg(opts)
return useNative(opts)
? mkdirpNative(path, opts)
: mkdirpManual(path, opts)
}
const mkdirpSync = (path, opts) => {
path = pathArg(path)
opts = optsArg(opts)
return useNativeSync(opts)
? mkdirpNativeSync(path, opts)
: mkdirpManualSync(path, opts)
}
mkdirp.sync = mkdirpSync
mkdirp.native = (path, opts) => mkdirpNative(pathArg(path), optsArg(opts))
mkdirp.manual = (path, opts) => mkdirpManual(pathArg(path), optsArg(opts))
mkdirp.nativeSync = (path, opts) => mkdirpNativeSync(pathArg(path), optsArg(opts))
mkdirp.manualSync = (path, opts) => mkdirpManualSync(pathArg(path), optsArg(opts))
module.exports = mkdirp

Some files were not shown because too many files have changed in this diff Show More