First jameswitzema.net commit

This commit is contained in:
Lao Tzu
2026-05-29 11:30:10 -07:00
commit 32cc108c4a
200 changed files with 1200635 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
Webroot for James' blog!
Goals for this page:
- Repo for projects and
thoughts
Hosting music! important function
- Should look flashy for employers
- Place to fuck around with programming
The plan for the PHP layout is as such:
1. index.php loads in header.php
2. index.php loads in some content
3. index.php loads in footer.php
Based on the URI, the PHP scripts will serve
text content. My fun little goal here is to
have PHP interpret the content as HTML, Markdown,
and raw text AT THE SAME TIME. This may not work.
# FOLDERS
- css: holds the main stylesheets.
- javascript: holds the main javascript.
- php: holds main php.
- content: holds main text content for the site.
+23
View File
@@ -0,0 +1,23 @@
<h1>|= ABOUT JAMES</h1>
<p>
Caden James Witzeman is a programmer and jazz musician
based in the US! James likes progamming in C, PHP,
systems administration, Gentoo, DWM, and other forms of
linux-based pain. James was trained in computer engineering
for 3 years for college, but didn't finish.
</p>
<p>
James plays mostly piano, and knows a bit of guitar. He started
playing keyed percussion in high school and later at Northern Arizona
University. James is currently listening to lots of Herbie Hancock
(specifically Mr. Hands and Future Shock),
Thelonious Monk, 9th Wonder, Victor Vaughn, and is <i>obsessed</i>
lately with Hiromi's <i>OUT THERE</i> album. Really good stuff.
</p>
<h3>LINKS</h3>
<ul>
<li><a href="mailto: me@jameswitzeman.net">me@jameswitzeman.net</a></li>
<li><a href="https://jaufheben.bandcamp.com/">Bandcamp</a></li>
<li><a href="https://github.com/jameswitzeman?tab=repositories">GitHub</a></li>
<li><a href="https://www.instagram.com/xu4nde/">Instagram</a></li>
</ul>
+101
View File
@@ -0,0 +1,101 @@
<title>I AM IBM</title>
<body>
<div id="body">
<h1>BUILDING A 6502-BASED COMPUTER</h1>
<h6>9/1/2023</h6>
<p>
Over the summer of 2023 I decided to keep myself busy with a relatively ambitious project;
I wanted to build an 80s-style terminal computer based off of the classic 6502 processor chip,
and I wanted to build a graphics processing circuit capable of terminal-style text-based video
output. This project essentially followed <a href="https://eater.net/6502"> Ben
Eater's 6502 computer video series,</a> with some minor tweaks and additions of my own. I
haven't even come close to a conclusion for this project, and I want the final product to be
printed on a custom PCB with a nice-looking enclosure (probably an old PC case retrofitted
for this purpose.)
</p>
<p>
The MOS 6502 processor was one of the earliest processor chips that were affordable enough
for large-scale home PC and console manufacturing. Early computer and game consoles such as
the apple I and II, Nintendo Entertainment System, Commodore 64, and many of the 8-bit atari
home computers used the 6502 processor or a similar variant. The original MOS chips are out
of production, but Western Design Center manufactures a near-identical 6502 chip with some
modern amenities such has higher max clock speeds and easier halting protocol. This is the
chip Ben Eater uses in his project and I will be using the same one.
</p>
<p>
In Ben's guide, he primarily uses a serial connection to a modern computer for interfacing
with the terminal, instead of implementing standalone graphics circuitry. He does include a
tangential project where he builds a rudimentary graphics system that supports color but
requires that the CPU write each pixel to the frame buffer manually, and uses a very rough
but straightforward method to provide DMA to the graphics unit. This is the main area where
I would like to insert my own improvements.
</p>
<p>
For my project, I want to implement basic terminal-style graphics support, similar to the
graphics functionality of the original Apple I computer. I am not interested in color support
whatsoever, I just want the CPU to write ASCII characters into the frame buffer in black and
white. I believe that when color and pixel-by-pixel frame writing are scrapped, my video
system could easily support much larger ASCII-based frame buffers at higher resolutions.
</p>
<p>
But before I could get started on the video system, I had to actually build the 6502 computer.
My computer is pretty much the same as Ben's, except I really had an issue with his memory
address allocation for peripherals. He sacrificed a lot of address space for the sake of
ease of implementation, and I wasn't as concerned about ease of implementation. Ben managed
his addressing with a few NAND gates. I upgraded to a set of demuxers/decoders so that
more specific address ranges could be reserved. I also halved the amount of ROM space
and doubled RAM space, just because I doubt any program on this computer will exceed
a few kilobytes. I kept the peripherals; A general I/O chip (6522), serial chip (6551), and
an added graphics processor. My new addressing logic allows for a greater addition of
peripheral however, and I'd like to add a few DAC's at the least. The last step of building the
computer was getting Wozmon working. Wozmon was an extremely rudimentary kernel designed by
Steve Wozniak for the Apple I which allowed for program writing (In hexadecimal assembly)
and executing, as well as browsing the address space. After I was able to confirm Wozmon was
working via a serial connection, I was ready to get started on my graphics system.
</p>
<p>
Fortunately, in order to implement graphics, I don't need to diverge much from Ben's video design
except for the last bits of the logic chain. The largest portion of the complexity of the
design comes from the logic that reads the pixel-by-pixel frame buffer and transmits the buffer
to the screen via a VGA signal. At the end of this logic chain is a system that reads the buffer
from memory. This end ought to be replaced by a ROM which contains the pixel-by-pixel ACSII
character set where each line of a character is addressed by its corresponding ASCII code
and the line number that should be displayed. This means the ROM's address lines should
be tied to the video system (which counts lines), and the frame buffer in memory. If the width
of each character is 8 bits/pixels, then this means the ROM will have to access memory directly every
8th pixel on the screen. I plan to run the video system at 40MHz, which means
every 8th pixel occurs every 200ns, or at a frequency of 5Mhz. Before that 8th
pixel is written by the video logic, some system must halt the 6502 (or otherwise stop it from
accessing memory), read from the proper address in memory, increment that address before the
start of the next cycle, make sure that the ROM has had enough time for its output to stabilize,
and shift each bit from the ROM into the VGA signal. This time constraint is the greatest
hurdle to this design, although I still believe I may be able to find parts that can run this
fast. Once I can actually write a character to the screen (via hard-coding and not DMA), then
I have to find a way for the processor to be able to run for some portion of this time. This
means that the graphics processor needs to access the RAM for as little time as possible. This
problem iz significant, and may require the pixel frequency to be halfed or quartered
to reduce resolution and allow the hardware more time. As of writing, I have built this video
circuitry without DMA, but have been unable to get a character to output to a screen. I
believe this is a timing issue, and will update once I have solved the issue.
</p>
<p>
This is the state of my project so far. But before I conclude, I would like to lay out my plans
for this project once the hardware is working. I would like to use the computer to interface
with analog synthesis circuitry to mak some kind of roided-up sound card for music. I would
also like to get a more complex operating system running on the computer. I would start this
process by getting a C compiler running on the 6502, and then getting some kind of basic Unix-
like kernel up and running. This is likely overambitious and I suspect I will just end up
getting BASIC running on it instead, but these are the end goals for the project.
</p>
<p>
This project has been very valuable to me in several ways. Building a computer from chips,
getting to the point of writing code for it in assembly, and seeing a basic kernel running
really helps build a rigorous understanding of how computer code really is just a high-level
abstraction from the bare metal hardware. I think with modern computers and their complexity,
this is a kind of holistic understanding that is lost. The hardware becomes a black box which
just runs code. This project helps remind me that no matter how complex our computers get,
at the end of the day they really do just execute binary instructions.
</p>
</div>
</body>
+6
View File
@@ -0,0 +1,6 @@
<title>BLANK</title>
<body>
<div id="body">
<h1>HEADER.</h1>
</div>
</body>
+58
View File
@@ -0,0 +1,58 @@
<title>PALESTINE</title>
<body>
<div id="body">
<h1>THE GENOCIDE IN PALESTINE</h1>
<p>
As of May 2024, nearly 50,000 Palestinians have been murdered by the Israeli government. Nearly all of
these lives were innocents. About 14,000 of these lives were children. There is absolutely no excuse
or justification for this fact. The fact of the matter is as follows:
</p>
<p>
Since its foundation ~70 years go, the nation of Israel has dedicated itself in part to the
extermination of the Palestinian people regional to the area. This has never ceased to be true.
Today, and since October 7th 2023, we see another example of Israel's commitment to genocide.
The far right Israeli government under Benjamin Netanyahu has a near-outright bloodthirst for children.
They want all Palestinians dead and they will not stop until there is nothing but rubble left in Gaza.
Then, they will move on to the West Bank. Anyone who challenges the idea that the West Bank is safe
from occupation because Hamas does not reside there is woefully unaware of the reality of the situation.
Although presently the bulk of Israel's occupation is focused on Gaza, they have countless times
aggressed on the West Bank. Hamas exists in part as a result of Israel's effort to prevent the spread
of the PLO to Gaza, to keep Palestinian people from uniting effectively in resistance.
</p>
<p>
It is FALSE that if Hamas laid down their arms, this conflict would end. This is false because the
bulk of the conflict has not been directed at Hamas, it has been completely <i>undirected.</i>
Israel chooses to intentionally level Gaza with indiscriminant bombing. This is not the strategy
of a military force trying to fight an enemy in a populated civilian area. It is the strategy of
genocide.
</p>
<p>
So what is the solution? Two-state? One-state? Frankly, I don't know. I know there will only be peace
once ISRAEL commits to a cease-fire, and when ISRAEL commits to allowing Palestinians in Gaza the same
rights and access to the democratic process as Israeli citizens. The current state of affairs <i>is</i>
a one-state solution. Israel is one state, and it has chosen to abandon millions of its citizens in
Gaza and the West Bank in the name of 'autonomy.' What a sick joke.
</p>
<p>
The only reason Hamas exists and commits terror attacks is because Israel has raised a generation of
Palestinians without families, without food and water and basic human respect. Of course this generation
has been driven to acts of terror, because Israel has terrorized them and their family daily for
generations. Additionally, this radicalization process has been inflamed by Israel as Israel has
destroyed every attempt by Palestinians to direct their suffering and agitation towards effective
organization. They bomb Gaza until all that is left is guns and hate and pain. Israel has the power
to stop the violence, and they choose instead to kill indiscriminantly. Our country (the United States)
refuses to revoke funding and support, we enable the genocide. Any rational humane actor will conclude
that this genocide must be stopped by any means necessary. I must add that wanton terror against Israeli
civilians is NOT an efffective means to stop the genocide. It is a war crime. It is a war crime when
Israel does it by the tens of thousands, and it is a war crime when Hamas does it by the hundreds.
For this reason, I cannot include wanton terror within 'any means necessary.' Wanton terror is not
a necessary means.
I can include many other forms of resistance, some of which
are violent. Sometimes occupation and oppression must be resisted violently. Our founding fathers
understood this very well when they violently resisted the British government. They added a Second
Amendment to our constitution to enshrine our right to maintain arms in the event that our
government must be violently resisted. I say, this issue is the same. I say to you, reader:
</p>
<h1>CEASEFIRE NOW.</h1>
</div>
</body>
+20
View File
@@ -0,0 +1,20 @@
<title>BLANK</title>
<body>
<py-config>
packages = [
"pandas",
"bokeh",
"xyzservices"
]
</py-config>
<div id="body">
<h1>HEADER.</h1>
<py-script>
import json
import pyodide
from js import Bokeh, console, JSON
p = figure(width=400, height=400)
</py-script>
</div>
</body>
+140
View File
@@ -0,0 +1,140 @@
<title>MACHINE LEARNING IN C</title>
<body>
<div id="body">
<h1>LEARNING NEURAL NETWORKING IN C</h1>
<h6>10/19/2023</h6>
<p>
To keep myself entertained between classes this fall semester (2023), I decided to try to learn a
little about machine learning mathematics. I've heard of the Python library TensorFlow, which does
a lot of the heavy lifting for you (in c++, so likely not at the expense of Python's slow interpreter).
I am interested in learning these types of python libraries, but first I wanted to develop a very
strong familiarity with the basics and mathematics of machine learning. I also am trying to get
as familiar as possible with C for my own personal enrichment. As of starting this project, I thought
it would be nice to have a machine learning library in C for any of those who didn't want to use
python for whatever reason. I realize now that this was foolish. My code probably doesn't have much
utility for anyone else, but it was a very good learning experience for me.
</p>
<h3>DOING MY RESEARCH</h3>
<p>
I started my journey by scouring a
<a href="http://neuralnetworksanddeeplearning.com/index.html">book by Michael Nielsen</a> on machine
learning. The book is a few years old and behind the times now, but I'm not going to be setting the
state of the art here so I'm not too concerned. I quickly learned that training a neural network really
is just a minimization problem, and the whole thing can be elegantly expressed as pure math instead of
as a programming problem. I started taking notes like I was in any other math class. The first big
concept to learn was gradient descent.
</p>
<p>
For those who have taken a multivariable calculus class, gradient descent is a pretty straightforward
exercise. For those who haven't, the core idea is still not too inaccessable. We can think of
a neural network as a huge, crazy black box with a million different parameters to tweak. We throw
a bunch of data into the box with an idea of what we want the network to spit back out. As long as we
can quantify how far off our network is from the ideal output, we can use this quantity as a function
of these million parameters. The idea here is to find the multivariable derivative of this error metric,
usually called a cost function. If we can find a method to figure out how tweaking each parameter
changes our output, then we can use that information to tweak them in just the right way to minimize the
cost and maximize how close our output is to the ideal. This multivariable differential is called the
gradient. Once we have our table of which parameters to tweak and in what way, we then choose how large
a step to take and we actually change all of our parameter values. This is very similar to the idea
of walking down a hill until you reach the lowest valley you can find. Because the network is a
complex mathematical entity with many moving parts, the hard part is figuring out how to even calculate
this gradient. That's where backpropagation comes in.
</p>
<p>
Backpropagation is the method by which a gradient is calculated for a neural network. The important
thing to remember is that all backpropagation does is find the gradient of the cost function. The
complicated part is figuring out how every single parameter should be tweaked to minimize the cost.
The first step is to figure out how the output should change in order to minimize the cost. Usually this
means the output will have a random set of activations that do not correlated to the input at all.
Our cost will likely be high. We know that the cost is a function of each output neuron's activation,
so we can take the derivative of the cost with respect to each activation value. Once we have this
differential, we then think of our output activations as functions of the activation of the previous
neuron, as well as the weight and bias for the current neuron. We can find this differential with
respect to these values. We then consider the activations of the previous neuron as functions of the
parameters of the neurons before that, and so on and so forth. This is why the process is called
backpropagation, we start by looking at how the output ought to change and work backwards from there.
The complicated part is the nuts and bolts of the math required to compute all this, which I will
not cover here. I highly recommend checking out Nielsen's online book.
</p>
<h3>IMPLEMENTATION</h3>
<p>
I started my program by building structures to hold all the neuron, weight, and bias data for each
layer. My goal was to have one structure which holds the entirety of the network for ease of reference.
I decided to use a structure to represent a single layer in a network. This structure holds vectors
which represent the data for each neuron, and a matrix for the weights. I used the GNU Scientific
library for my vector maths, which may have been a little overkill but it allows me to work
with complex math structures easily. It will also help when I expand into convolutional networks.
</p>
<p>
Once I was sure I had my network data structures working, I started building code for importing data
into the network. The example problem I used to get the network functioning was the classic digit
recognization problem. The idea is to train a network to reconize and correctly categorize a low-
resolution image of a handwritten digit, in black and white. In order to import an image, I
had to open the file containing the images and scan through each line (where one line is one image)
and import the 0-255 greyscale values into my dayta structure. Firstly, the network only works
with values from 0-1, so I had to map the 0-255 values to 0-1. I decided to use a simple linear
mapping between values, as opposed to some non-linear function like the sigmoid. I did experiment
with the sigmoid function once the network was working, but the results were not as good. I suspect
this is because the sigmoid is capable of mapping an infinite domain of values to a range of 0-1,
meaning you could plug in an arbitrarily large number in and get a number restricted to the finite
range out. This is very useful for calculating network activations, when the weights and biases can
sum up to very large numbers. But if your data input is already restricted to a finite domain,
then a simple linear map is better. Long story short, after some tweaking I had the image data properly
converted and imported to my network's input.
</p>
<p>
The next task was to get my network to properly <i>feedforward</i>, that is, to properly calculate
the activations for every neuron in every layer up to the output. Of course, the untrained network
will output nonsense, but the important part is that it is properly calculated. The calculus will not
work otherwise. Once the output can be calculated from the input data, then the cost needs to be
calculated. I started using mean squared error for my cost function, then switched to log-likelihood
later. I know the cost is working properly if it is very high for the untrained network, usually
in the ballpark of 1-3. I can't really know for sure that the cost is working until I see it going
down during the training process, but I was confident at this point it was doing okay. Once this was
all up and running, I could move on to backpropagation.
</p>
<p>
The first really serious programming challenge was getting backpropagation up and running, although
because the algorithm is so well-documented it boiled down to effectively implementing the algorithm
in code. The first step is to find the <i>'error'</i> of the output layer, which really just means
figuring out how the output should change to decrease the cost. Then the error for the layer before
it is calculated, and so on, until we get to the input layer. The input activations cannot be controlled
by the network of course, but we can control the weights and biases connected to the inputs. We
can figure out how the weights and biases ought to change, then we tweak them. This is the gradient
descent step. We choose a value for the small step we should take, then we change the value
of each weight and bias by the product of that step and the corresponding differential. The idea is
analogous to stepping down a hill, but if the hill was a surface in a 13,000-dimensional space.
We know that these steps are working if we watch the cost go down after each descent. After many hours
of watching the cost jump around or not move at all or return a divide by zero error, I eventually
was able to get my program to descent properly and actually recognize features of different digits.
</p>
<p>
Watching the network's cost function minimize is extremely satisfying and important, but it doesn't
matter at all if we aren't actually categorizing digits properly. It is possible for the network
to get stuck in small valleys of its cost function, where it categorizes all data the same way
and gets a mildly acceptable cost, or to spit out 0 on all of its output neurons, or something like
that. It's also common for the network to learn to recognize the training dataset with 100% accuracy,
but when shown new data it wasn't trained on it gets totally lost. This is sort of like the difference
between memorization and actual understanding of a concept in human brains. So in order to avoid this,
we show the network a set of digits it wasn't trained on and test how many it correctly categorizes.
This gives us a direct accuracy metric that shows us how well it has actually learned, instead of
'memorizing' the dataset. The general task list for my program is to train the network, test the
network, and return the score so the user can tell how well the network has learned. The first
version of my code got an accuracy of around 75-80%. After implementing some better cost functions,
better activation functions, and tweaking the backpropagation a little, I was able to get
around 90% max accuracy. This is not nearly as high as Nielsen's program, which achieved around 95-96%
with the same methods. Frankly, I am still trying to figure out where the disparity comes from. I am
relatively happy with the network so far however.
</p>
<p>
The next step for this project is to implement a <i>convolutional</i> neural network, which is
a different architecture specialize in image processing. This requires a lot more math and complexity,
but it is doable (I think.) Currently, I have been able to get the data structure up and running,
but I have not implemented feedforwarding and backpropagation. I will make the code available
on my GitHub once I can get this working. So far, the project has been extremely educational for me,
not only on the math of machine learning but also for my general literacy in C. I would recommend
that any software engineer with some spare time and hunger for learning and challenge try this
themselves.
</p>
</div>
</body>
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

+101
View File
@@ -0,0 +1,101 @@
<title>MODULAR SYNTH</title>
<body>
<div id="body">
<h1>BUILDING A SYNTHESIZER FROM SCRATCH</h1>
<h6>10/30/2022</h6>
<p>
This project got started the summer of 2022 when I got really into analog circuitry and additionally realized I would not have enough money
to buy my own synthesizer for several years. I stumbled upon the Youtube channel of <a href="https://www.youtube.com/c/MoritzKlein0">Moritz Klein,</a>
a synth hardware designer who makes educational videos on synth circuitry in his free time. This inevitably lead me down an endless rabbit hole that I may never escape from.
Since then I have spent endless hours researching oscillators, exponential converters, ladder filters, SR-latch envelope generators, etc and I have learned a lot about audio
hardware. This page will be a tome to my learning and plan to update it regularly. Let's jump in!
</p>
<h3>STEP ONE: THE VCO</h3>
<p>
When it comes to synth hardware, probably one of the most vital circuits is the Voltage Controlled Oscillator, or VCO. As the name implies, the VCO is an oscillating circuit whose
pitch can be controlled by a voltage input. VCO's usually offer sawtooth, square, and triangle waves as standard base waves. This task is relatively simple, but trying to build a robust VCO with
consistent tuning, wide range, and accurate voltage control turns out to be quite a difficult task. This is something I learned the hard way.
</p>
<figure class="centerimg">
<img src="4069vco.png" />
<figcaption>4069 VCO schematic mine was based off of.</figcaption>
</figure>
<p>
The first guide I followed was Klein's VCO video, which is based off of <a href="https://www.schmitzbits.de/vco4069.html">Rene Schmitz's VCO 4069.</a> I quickly learned that Schmitz's website would be a prime resource
for synth circuit learning: it contains many different circuits for just about every essential synth part. This VCO is based off of a 4069 Hex Schmitt Trigger CMOS chip, a pretty classic chip in the DIY synth
scene. It is comprised of three main sections: The input processing, oscillator, and wave shaper. The input processing sums each of the several voltage inputs, and then sends them to the exponential converter. Because
musical pitch is exponential and per the V/Oct standard one volt corresponds to one octave, the converter must turn the linear voltage to an exponential current for proper tracking.
Next is the oscillator, it takes the current and spits out a sawtooth wave. Finally is the wave shaping, which converts the sawtooth to a square wave with pulse width
modulation. The circuit is pretty straightforward, but I couldn't get it to work and I had issues with its thermistor temperature compensation.
</p>
<figure class="wrapimg">
<img src="schmitzexpco.png" />
<figcaption>Common exponential converter transistor setup.</figcaption>
</figure>
<p style="margin-bottom:7vh;">
The main reason I moved on from this circuit is because I coulnd't get the exponential converter to work with the 4069. I think the issue was a poorly matched set of transistors, but regardless I got stuck.
So I abandoned the converter and went off to research the circuitry. I learned that this circuit's converter is rather unusual for using a PNP and NPN. Most converters I found used matched NPN or PNP transistors
to convert the linear voltage. The next thing I tried was just swapping the converter from the original with a version of this one, which worked fine. The V/Oct tracking wasn't great and the circuit only uses one trigger
alongside a whole bunch of opamps. When I tried to scale this circuit up to utilize all six schmitt triggers, I quicly realized the circuit became way too big to use. I decided to move on to a different circuit.
</p>
<figure class="wrapimg", style="display:inline;">
<img src="vconeu.png" />
<figcaption>The final schematic I went with, built around a 555 timer.</figcaption>
</figure>
<p style="margin-bottom:10vh;">
At this point, I'd discovered Schmitz's website and was looking through his VCO designs. After considering the viability of each one, I decided to build his <a href="https://www.schmitzbits.de/vco2.html">VCO 2,</a> a 555-timer-based VCO with a pretty simple schematic.
After about two weeks of troubleshooting and trying to understand the circuit, I finally got a working VCO with a square and saw wave output, decent V/Oct tracking, and a sensible count of chips!
Next, I decided to take the circuit off the breadboard and build a stripboard prototype. I've considered designing and ordering
an actual PCB for this circuit, but because most PCB outfits require an order of at least 5 or 10 boards and I really have
no need for that many, I'm gonna wait on doing it the proper way until this project is a little more concrete. So instead I
sketched up a board layout I was happy with (I did this on paper, I just couldn't find a decent stripboard layout software),
I soldered it all together! Of course, it didn't work right off the bat and I had to do some troubleshooting, but eventually
I got it working as expected. All that's left now is to cut some aluminum for a faceplate that I can screw into a synth rack.
</p>
<h3>STEP TWO: ENVELOPE AND VCA</h3>
<figure class="wrapimg">
<img src="vcaneu.png" />
<figcaption>Schmitz's VCA circuit I used for my synth.</figcaption>
</figure>
<p>
Now I have an oscillator that can be tuned and used to play melodies, but there's a pretty glaring problem with it:
It's always on! I don't have any system for actually triggering the VCO on or off by presisng a button or hooking
up a sequencer. I need a Voltage Controlled Amplifier or VCA to gate the volume on or off. The VCA is pretty simple,
it's just a voltage-controlled volume knob that I could hook up to a gate signal to control the volume like a piano key.
Again, I turned to the Schmitz page for this. His <a href="https://www.schmitzbits.de/vca.html">VCA</a> design is also a pretty common one, based on that same matched
transistor pair to allow for a voltage control of the signal. VCA circuits tend to be pretty straightforward, and all the other
components are just input and output formatting, with the other three transistors just processing the CV to work properly with
the amp. The opamp similarly just serves as an output buffer. I've got this circuit on my breadboard right now (11/2/22) and
it works well, although it doesn't seem to completely silence the signal at CV 0V, so I will experiment with it further before
putting it on a board.
</p>
<p>
The convenient thing about this design for my purposes is that it only uses a single opamp, which for most opamp chips means
that there will be at least one unused amp. Schmitz accounts for this by using a single-opamp chip in his design, but I don't
have anything like that on hand so I ended up using the classic TL072 chip. To take advantage of this, I decided to build an
envelope generator with the second opamp and let this module be a two-in-one VCA-Envelope because the two already pair together
quite well.
</p>
<p>
An envelope generator takes in a gate signal and converts it to a more complex four-stage signal. It smoothes out the rising
edge of the gate into an "attack," then immediately goes into a falling "decay" until it settles on the "sustain" level.
I will stay at its sustain level until the gate is released, then the falling edge of the gate is converted into a "release."
This allows a gate signal to produce a much more musical control voltage and can imitate the sharp attack and slow release of a
guitar string, or the schmultzy slow attack and release of a bowed violin, etc. This is an essential function of any synthesizer.
</p>
<figure class="centerimg">
<img src="adsr.png" />
<figcaption>Schmitz's 555-based ADSR envelope. One opamp, yay!</figcaption>
</figure>
<p>
Surprisingly, the envelope is probably one of the simplest circuits to build yet. The most basic form, which just has an attack and release,
just charges and discharges a capacitor through two diodes to separate the two stages. To implement a decay and sustain, an SR
latch is often used to control when the circuit enters its decay phase. <a href="https://www.schmitzbits.de/adsr.html">Schmitz's schematic</a> uses the SR latch inside the 555 timer
(alongside its other features) to do basically all of the heavy lifting, and the rest of the circuit is just resistors, transistors,
and pots for controlling the ADSR. Perhaps most importantly, the circuit only uses one opamp! This means I can build it on the
other side of the TL072 and have a nice combo module for my synth. Also, the two circuits are so simple I might be able to fit it
onto a tiny stripboard and save some space (although all the knobs and patchcable sockets may foil my plans). This is my final plan,
and once I get that issue with the VCA fixed I'll put it all on a board and be finished with a good chunk of this synth!
</p>
</div>
</body>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+108
View File
@@ -0,0 +1,108 @@
<body>
<title>MY PAGE</title>
<div id="body">
<h1>THIS PAGE</h1>
<h3>PROGRAMMING THE LIKES OF WHICH NASA HAS NEVER SEEN</h3>
<h6>3/24/2024</h6>
<p>
Hey, what's the deal with the URL up there? It's not just a simple filesystem, there's a
question mark and some text. What's going on?
</p>
<p>
The site may look effectively the same, but the backend has been completely overhauled and launched into 1996!
The old architecture was a basic filesystem hosting static html with a little bit of client-side javascript.
This was acceptable, but as my ideas increase in complexity I realize that making new pages is more and more of a pain
with this old setup. So welcome to the future!
</p>
<p>
Now all page content is managed by PHP and served up by an internal SQL server! This means in order to add a page,
I just have to write the html for the body of text and PHP deals with the rest. It also makes my system more modular.
I can specify the type of page (article, video page, image gallery, interactive app, etc) to change the styling,
format, header/footer, etc. of the page. I can also add entirely new page types whenever I please and I can
specify what to do when a type is given. At the moment, the only implemented page type is the simple tex article like
what you're reading now. But I can make new ones as I please. Pretty cool!
</p>
<p>
Using a LAMP stack system also allows for <i>dynamic</i> webpages! I can pull in data and then swap it for something else!
I can get videos up and running, or send machine learning data back and forth for fun interactive ML toys! Fun stuff is
abound...
</p>
<h3>UPENDING THE ORDER</h3>
<h6>2/1/2024</h6>
<p>
Starting October 2023, I began working as an all-purpose programmer for the USGS in Flagstaff. Effectively,
my job is to run their web services and help develop data visualization apps for them. As a refult, I have
learned a lot about modern web architecture. Looking back here at this website, I realized an overhaul was
in order. So over the last few weeks, I've been fixing up the backend.
</p>
<p>
The main thing I've done so far was to get each service and put it in a Docker container which pulls all its
web content from Github. This means each service can be invididually controlled and deployed on any machine
and setup can be highly automated and synced quickly. This makes everything easier.
</p>
<h3>10/10/2022</h3>
<p>
This humble website I've made to host my portfolio is the first serious trek I've made into web design.
Of course, I've played around with style sheets and javascript here and there, but this is the first time I've tried to make
something polished. I learned some about neat css styling, javascript, and a lot about mobile web design.
The site is not at all perfect (I threw the first version together in a week with only a little knowledge of web design), but it's
something I'm actually happy with and even a little proud of. Hopefully this page will give some insight to my journey down the web-hole.
</p>
<p>
I decided to make a webpage for several reasons:
</p>
<ul class="number">
<li>
I realized my resume is pathetic and doesn't really show off any of my programming skills as of now (being a second-year engineering student with no field experience),
</li>
<li>
I thought it would be a fun challenge and learning experience to flex my dusty web design muscles on a project that has real utility,
</li>
<li>
I was bored of killing my free time playing Disco Elysium and wanted something to do.
</li>
</ul>
<p>
So I sat down and started throwing some markup together!
</p>
<h3>THE JOURNEY</h3>
<p>
The first thing I wanted to do was make a really slick home page that was like a night sky overlooking a synthwave landscape thing.
The problem was, I can't draw nor do I have experience in graphic design, so I wrote up some scripts for making a night sky and a gridded landscape.
The landscape was the easy one. All I did was draw some horizontal lines with a distance between them that decreases as they approach the horizon , then draw angled lines radiating from the horizon and BOOM! done.
Next was the starry night. For full detail on this side-project, see my page on it <a href="starsky.html">here</a>.
I basically just draw stars with random temperatures, sizes, and locations. I also have a cluster drawn along a randomly defined line, to simulate a galaxy.
As of writing I'm not done with this night sky project, I have plans to add more galaxy clusters and nebulae and all that. But that's for another time.
</p>
<p>
Next, I had to actually put this pictures onto a webpage. I wanted the viewport to be filled with the night sky and a title heading: 'JAMES WITZEMAN.'
Then if the user scrolls down, they're taken to the land where the navbar is. I also wanted there to be a nice parallax effect between the sky and the landscape,
to really make it feel like it was behind the land. This was challenging part for me to wrap my head around. What I ended up doing was employing the z-index and transform attributes to get the desired effect.
The end result is a simple but pretty home page. Next, I want to include some 'fun' activities to play around with on the homepage (if I find time).
</p>
<p>
The next challenge was trying to make sure the site loads properly on mobile. I probably could have just built a style sheet that can flex enough to work on a totally different viewport,
but frankly I am not capable enough for that. So instead I decided to throw a little javascript together that detects if the page is loaded with a giant regex that returns true if
the device is a phone. If the device is a smartphone, then the javascript replaces the main css pointer with a different pointer to a mobile css file. The mobile file has everything arranged vertically,
instead of a standard sort of desktop blend of wide bodies and left-to-right navbars. To get a feel for what a readable text-heavy mobile page looks like, I tried to take some pointers from Wikipedia.
Wikipedia's mobile pages are text-heavy, but the text is often broken by paragraph spaces, lists, photos, and title headings. The text is also in a no-abrasive font, the line spacing keeps the paragraphs
from being too dense and intimidating, and the margins are tight. I tried to lift as much of this advice as I could to keep my pages readable.
</p>
<p>
Finally I had a somewhat readable page on mobile, but the navbar was totally broken. It was made for a page with planty of horizontal space to fill, but on mobile there's barely
space for a single link. So I decided to build a simple drop-down bar for mobile only. This meant I needed to add some kind of single button or element that, when clicked, reveals a hidden vertical navbar.
Because the original HTML doesn't have any kind of button element, I needed to add it in wih JS when a mobile device is detected. I threw this in the main javascript file, and added some code that
reveals the vertical navbar when the button is clicked. Now I have a nice mobile dropdown!
</p>
<h3>THE END; THE FUTURE</h3>
<p>
So that's about it. As far as the website goes for now, that's all I've gotten to. I think it's been a rewarding challenge for myself and I learned some about how all this works.
The website is still pretty barebones, and I want to try to add some fun physics stuff, encryption and security, a blog-editor GUI so I don't have to type all this in raw HTML,
and maybe a login-password system for fun. Until then, that's all for now I think. Thanks!
</p>
<p>-James</p>
</div>
</body>
+350
View File
@@ -0,0 +1,350 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html >
<head><title>Oscillators</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="generator" content="TeX4ht (https://tug.org/tex4ht/)">
<meta name="originator" content="TeX4ht (https://tug.org/tex4ht/)">
<!-- html -->
<meta name="src" content="oscillators.tex">
<link rel="stylesheet" type="text/css" href="oscillators.css">
</head><body
>
<h3 class="sectionHead"><span class="titlemark">1 </span> <a
id="x1-10001"></a>Oscillating Circuits</h3>
<!--l. 12--><p class="noindent" >A discussion of synthesizer oscillators requires an introduction to simple oscillating circuits. We
will discuss the most basic oscillating circuits, then we will move on to oscillators with
easily-tunable frequencies. Then we will tackle the most complex issue for the purposes of musical
synthesis: <span
class="ecti-1000">voltage control. </span>This is the important part! If we can control an oscillator&#8217;s frequency by
voltage, then we can make another circuit change its voltage, like a sequencer for example. Let&#8217;s
check it out!
<!--l. 18--><p class="noindent" >
<h4 class="subsectionHead"><span class="titlemark">1.1 </span> <a
id="x1-20001.1"></a>Passive Oscillators</h4>
<!--l. 20--><p class="noindent" >The simplest oscillators are those which rely on <span
class="ecti-1000">passive components</span>, electrical components which
do not generate power or &#8217;add amplitude&#8217; to a signal. These are components like resistors,
inductors, and capacitors which only dissipate, store, or release already-existing power introduced
by another component. An example of a non-passive component would be a power supply or a
transistor. Passive components tend to be governed by simpler rules that are easier to understand
and exploit.
<!--l. 26--><p class="indent" > The simplest oscillating circuit to my knowledge is the Resistor-Inductor-Capacitor or RLC
circuit. It&#8217;s not an &#8217;RIC&#8217; circuit because the letter I commonly represents current in electrical
engineering, so we use L to indicate inductors or inductance.
<div class="center"
>
<!--l. 30--><p class="noindent" >
<!--l. 36--><p class="noindent" ><img
src="oscillators0x.svg" alt="RLC
" ></div>
<!--l. 39--><p class="indent" > The diagram above depicts an RLC-circuit, with each component in series. We can come up
with an equation to describe its behavior, but first we need to know how each component responds
to voltage and current. <span
class="ecbx-1000">Resistors </span>are governed by <span
class="ecbx-1000">Ohm&#8217;s Law</span>:
<div class="math-display" >
<img
src="oscillators1x.svg" alt="V = IR, I = V-,
R
" class="math-display" ></div>
<!--l. 43--><p class="nopar" > where <span
class="cmmi-10">R </span>is resistance, measured in Ohms (<span
class="cmr-10">&#x03A9;</span>). Resistors are called <span
class="ecbx-1000">linear components </span>because
their voltage-current response is linear i.e. an increase in voltage or current causes a linear increase
in the other. <span
class="ecbx-1000">Inductors </span>are governed by:
<div class="math-display" >
<img
src="oscillators2x.svg" alt=" &#x222B;
V = Ldi, i = 1 V dt
dt L
" class="math-display" ></div>
<!--l. 49--><p class="nopar" > where <span
class="cmmi-10">L </span>is inductance, measured in Henries (H). Note that this means that the voltage across an
inductor responds to a change in current. If a current is constant, then the voltage vanishes. But if
we change the current, a voltage is generated across the inductor. That means if we send an
<span
class="ecti-1000">alternating current </span>which is always changing through the inductor, then we will get a
voltage across the inductor. This is unusual because an inductor is essentially just a
short-circuit and yet when a changing current passes through it, it will have a voltage like a
resistive element! You could say that inductors <span
class="ecti-1000">resist a change in current </span>in this sense of
resistance.
<!--l. 59--><p class="indent" > Finally, <span
class="ecbx-1000">Capacitors </span>are governed by the equation:
<div class="math-display" >
<img
src="oscillators3x.svg" alt="i = C dv,
dt
" class="math-display" ></div>
<!--l. 60--><p class="nopar" > where <span
class="cmmi-10">C </span>is the capacitance of the component, measured in Farads (F). Here, the capacitor&#8217;s
behavior is similar but the relationship is sort of reversed or flipped, if you will. Now, if we have a
constant voltage across the capacitor then no current will flow. This makes sense because a
capacitor is essentially made from two conductive plates seperated from one another by a
non-conductive material. This is effectively a break in the circuit, as indicated by the standard
electrical symbol for a capacitor. But if we change the voltage across the capacitor, it starts to
conduct! changing the voltage somehow forces current to flow between the plates! It&#8217;s no mystery,
this is due to some complicated rules of physics known generally as <span
class="ecti-1000">electrodynamics</span>, but that&#8217;s for
another time.
<!--l. 69--><p class="indent" > Okay. We can connect these three equations mathematically by utilizing <span
class="ecbx-1000">Kirchoff&#8217;s Laws of</span>
<span
class="ecbx-1000">Voltage and Current</span>. This sounds a little complicated but it relies on some straightforward
principles. The core idea is that any current which enters a wire junction (usually called a <span
class="ecti-1000">node</span>)
must exit the junction in some way. For many reasons, another logical rule that follows from this is
that the voltages across each component in a loop must sum to zero. If this were not true, it would
result in charge pooling somewhere in the wire, which is almost always impossible. Here&#8217;s the
laws:
<ul class="itemize1">
<li class="itemize">
<!--l. 76--><p class="noindent" ><span
class="ecbx-1000">KIRCHOFF&#8217;S CURRENT LAW: </span>All current entering a node must exit i.e. the
sum of currents entering/leaving a node must always be <span
class="ecti-1000">zero.</span>
</li>
<li class="itemize">
<!--l. 78--><p class="noindent" ><span
class="ecbx-1000">KIRCHOFF&#8217;S VOLTAGE LAW: </span>The sum of component voltages over any loop
of wire must be zero.</li></ul>
<!--l. 81--><p class="indent" > If the current entering each node must also be leaving it, in our RLC circuit this means the
current through each node is the same. This is because the circuit is a closed loop! If the current
were not the same across each node, then it would have to pool somewhere or escape into thin air.
We can express this mathematically:
<div class="math-display" >
<img
src="oscillators4x.svg" alt="iR + iL + iC = 0.
" class="math-display" ></div>
<!--l. 84--><p class="nopar" > We know the equations for the current through a resistor and capacitor:
<div class="math-display" >
<img
src="oscillators5x.svg" alt="v dv
--+ iL + C --= 0.
R dt
" class="math-display" ></div>
<!--l. 86--><p class="nopar" > We know that voltage across an inductor is proportional to the change in current.
If we integrate with respect to time, we can get a figure for the current through an
inductor:
<div class="math-display" >
<img
src="oscillators6x.svg" alt=" &#x222B;
iL =-1 vdt
L
" class="math-display" ></div>
<!--l. 89--><p class="nopar" >
<div class="math-display" >
<img
src="oscillators7x.svg" alt="v 1 &#x222B; dv
R-+ L- v dt+ C-dt = 0.
" class="math-display" ></div>
<!--l. 90--><p class="nopar" > Now we just differentiate:
<div class="math-display" >
<img
src="oscillators8x.svg" alt="C d2v + 1-dv+ -1v = 0,
dt2 R dt L
" class="math-display" ></div>
<!--l. 92--><p class="nopar" > or
<div class="math-display" >
<img
src="oscillators9x.svg" alt=" &#x2032;&#x2032; &#x2032; 1 1
av + bv + cv = 0; a = C, b = R-, c = L-.
" class="math-display" ></div>
<!--l. 93--><p class="nopar" > I would like to state here that the variable <span
class="cmmi-10">v </span>is a function dependent on time, so it should be
written as <span
class="cmmi-10">v</span><span
class="cmr-10">(</span><span
class="cmmi-10">t</span><span
class="cmr-10">) </span>for clarity. We prefer to be unclear here, because it is less cluttered to write
equations that way. Just keep this in mind.
<!--l. 97--><p class="indent" > What we have stumbled upon here is a truth that I find quite exciting but it has been the
nightmare of many underclassmen electrical engineers forced to learn this before finishing their
math coursework (who could I possibly be talking about here?): This circuit is governed by a
<span
class="ecbx-1000">homogenous second-order ordinary differential equation! </span>Unfortunately we cannot go over
the basics of ODE&#8217;s here. Like many stressed undergraduate engineers before you, you
will have to take my word as gospel. A homogenous second-order ODE is basically
an equation where the function (<span
class="cmmi-10">v </span>in this case) is not defined directly in terms of an
independent variable like time or <span
class="cmmi-10">x </span>or space etc, but in terms of its own differentials. This
equation is <span
class="ecti-1000">second-order </span>because the function is defined in terms of its second differential.
If you are very clever, you may already be thinking of how one might solve such an
equation to find <span
class="cmmi-10">v</span><span
class="cmr-10">(</span><span
class="cmmi-10">t</span><span
class="cmr-10">) </span>in terms of <span
class="cmmi-10">t </span>alone and not in terms of its differentials. If you are
even cleverer, you may be thinking about the classic function <span
class="cmmi-10">f</span><span
class="cmr-10">(</span><span
class="cmmi-10">t</span><span
class="cmr-10">) = </span><span
class="cmmi-10">e</span><sup><span
class="cmmi-7">x</span></sup><span
class="cmmi-10">, </span>because its
differential/integral is itself. If you are some kind of genius, you may even be considering also the
trigonometric functions (sine, cosine, not so much tangent here), because they have a similar
property. This will put you on the right track. Going forward, you will find that the
equations that govern these circuits involve many natural exponentials and sine waves
because of this property. Most ordinary differential equations are solved using complicated
techniques that involve relating differentials with natural exponentials and waves. I
cannot get into specifics here but if you are interested, I would keep this property in
mind.
<!--l. 115--><p class="indent" > OKAY. Back to the equation. Our equation can be represented by something called its
<span
class="ecti-1000">characteristic equation:</span>
<div class="math-display" >
<img
src="oscillators10x.svg" alt="ar2 + br+ c = 0.
" class="math-display" ></div>
<!--l. 116--><p class="nopar" > This is just a representation of the equation that represents the order of each differential by a
power of the variable <span
class="ecti-1000">r</span>. If we solve it like a polynomial we get the quadratic formula:
<div class="math-display" >
<img
src="oscillators11x.svg" alt=" &#x221A; -2-----
r1,2 = - b±--b---4ac.
2a
" class="math-display" ></div>
<!--l. 119--><p class="nopar" > You might realize that it is possible for the root of our characteristic equation to be complex
(<span
class="cmmi-10">r </span><span
class="cmr-10">= </span><span
class="cmmi-10">&#x03BB; </span><span
class="cmr-10">+ </span><span
class="cmmi-10">&#x03BC;i</span>). When this is the case, our solution is of the form:
<div class="math-display" >
<img
src="oscillators12x.svg" alt="v(t) = C1e&#x03BB;tcos(&#x03BC;t)+ C2e&#x03BB;tsin(&#x03BC;t),
" class="math-display" ></div>
<!--l. 122--><p class="nopar" > where <span
class="cmmi-10">C</span><sub><span
class="cmr-7">1</span></sub><span
class="cmmi-10">,C</span><sub><span
class="cmr-7">2</span></sub> are constants determined by the <span
class="ecti-1000">initial conditions </span>of the system. Once all
the math is done, we will take a break to build some intuition for all this nonsense.
The important part is that the reason this is a solution has to do with the fact that
the differentials of exponentials and sine waves are equal to themselves or related to
themselves.
<!--l. 127--><p class="indent" > Now we can start getting specific with our constants. If we assume our system begins
with the components already charged (meaning we have allowed a current source to
keep a constant voltage across the components for enough time for the capacitor to
be charged to the positive voltage <span
class="cmmi-10">V</span> <sub><span
class="cmr-7">+</span></sub>), then we can say <span
class="cmmi-10">v</span><sub><span
class="cmr-7">0</span></sub> <span
class="cmr-10">= </span><span
class="cmmi-10">V</span> <sub><span
class="cmr-7">+</span></sub><span
class="cmmi-10">. </span>If it has sat for a
long long time, then there will be no change in voltage, so also we can say <span
class="cmmi-10">v</span><span
class="cmsy-10">&#x2032;</span><sub><span
class="cmr-7">0</span></sub> <span
class="cmr-10">= 0</span><span
class="cmmi-10">.</span>
So:
<div class="math-display" >
<img
src="oscillators13x.svg" alt="v(0) = V+ = C1, v&#x2032;(0) = 0 = &#x03BB;C1 + &#x03BC;C2,
" class="math-display" ></div>
<!--l. 131--><p class="nopar" >
<div class="math-display" >
<img
src="oscillators14x.svg" alt="C1 = V+, C2 = - &#x03BB;V+.
&#x03BC;
" class="math-display" ></div>
<!--l. 132--><p class="nopar" >
<!--l. 134--><p class="indent" > We can keep defining constants and it will quickly cause your mind to cloud over. The point
here is that if we charge up this circuit and then let it run, the voltage and current will oscillate
back and forth. I have included below a graph of a potential oscillation. The frequency of this
oscillation is determined by the constant <span
class="cmmi-10">&#x03BC;</span>, which is equal to the inverse of the root of
the product of inductance and capacitance (<span
class="cmmi-10">&#x03BC; </span><span
class="cmr-10">=</span> <img
src="oscillators15x.svg" alt="--1-
&#x221A;LC--" class="frac" align="middle">). In electrical engineering, it is
usually called the <span
class="ecbx-1000">angular frequency </span>and is more commonly denoted with the greek
character <span
class="cmmi-10">&#x03C9;</span>. We found the constants <span
class="cmmi-10">C</span><sub><span
class="cmr-7">1</span></sub><span
class="cmmi-10">,C</span><sub><span
class="cmr-7">2</span></sub> to show that they are dependent on the initial
voltage we charge the circuit to, as well as the properties of the three components. We
have shown mathematically that the behavior of the circuit under certain parameters
(such that <span
class="cmmi-10">b</span><sup><span
class="cmr-7">2</span></sup> <span
class="cmmi-10">&#x003C; </span><span
class="cmr-10">4</span><span
class="cmmi-10">ac</span>) will be oscillatory in nature. But why does the circuit oscillate
sometimes?
<div class="center"
>
<!--l. 147--><p class="noindent" >
<!--l. 148--><p class="noindent" ><img
src="./img/natural-RLC.png" alt="PIC"
width="21" height="21" ></div>
<!--l. 151--><p class="indent" > Well, recall that an inductor generates a voltage once the current through it changes, and that
a capacitor begins conducting current once the voltage across it changes. When we charge the
capacitor to some voltage and then close the circuit, the voltage across the capacitor suddenly
becomes the voltage across the resistor as well. When there is a voltage across a resistor which is
connected in a loop, then a current must flow. Conversely, the moment the circuit is closed it
forces the voltage to drop because a current must flow through the resistor. If the capacitor
were alone in series with the resistor, then it would simply discharge to a voltage of
0.
<!--l. 158--><p class="indent" > As the capacitor pushes current through the inductor, the inductor begins to respond. Initially
it acts as a short, but as the current through it changes, it begins to generate a voltage. That
voltage causes the inductor to push current into the capacitor again, charging it. Then once the
inductor has discharged its stored energy, the capacitor is recharged and it begins to conduct
again. This continues until all of the electrical energy is dissipated through the resistor (and
realistically also through the resistances in the capacitor and inductor) as heat until there is none
left. Kind of cool!
<!--l. 164--><p class="indent" > If we tuned the capacitance and inductance properly, we could get one of these circuits to
oscillate at an audible frequency. This would not be an ideal circuit for music making, and there
are two big reasons for this:
<ul class="itemize1">
<li class="itemize">
<!--l. 168--><p class="noindent" >The circuit will only oscillate for a short period after it is triggered, preventing us from
ever using it to play any sustained note.
</li>
<li class="itemize">
<!--l. 170--><p class="noindent" >YOU CAN&#8217;T TUNE THE FREQUENCY! You would need a variable inductor or
transformer, and those solutions quickly become impractical. Unless you like plucky
drone music, you&#8217;re out of luck here.</li></ul>
<!--l. 174--><p class="indent" > I&#8217;m sure there are many other kinds of passive oscillating circuits, but I think we have done
enough here. Next we will consider <span
class="ecti-1000">active oscillators.</span>
<!--l. 179--><p class="noindent" >
<h4 class="subsectionHead"><span class="titlemark">1.2 </span> <a
id="x1-30001.2"></a>Active Oscillators</h4>
</body></html>
+122
View File
@@ -0,0 +1,122 @@
/* start css.sty */
.cmr-7{font-size:70%;}
.cmmi-7{font-size:70%;font-style: italic;}
.cmmi-10{font-style: italic;}
.cmsy-7{font-size:70%;}
.ecti-1000{ font-style: italic;}
.ecti-1000{ font-style: italic;}
.ecti-1000{ font-style: italic;}
.ecbx-1000{ font-weight: bold;}
.ecbx-1000{ font-weight: bold;}
.ecbx-1000{ font-weight: bold;}
p{margin-top:0;margin-bottom:0}
p.indent{text-indent:0;}
p + p{margin-top:1em;}
p + div, p + pre {margin-top:1em;}
div + p, pre + p {margin-top:1em;}
a { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; hyphens: auto; }
@media print {div.crosslinks {visibility:hidden;}}
a img { border-top: 0; border-left: 0; border-right: 0; }
center { margin-top:1em; margin-bottom:1em; }
td center { margin-top:0em; margin-bottom:0em; }
.Canvas { position:relative; }
img.math{vertical-align:middle;}
div.par-math-display, div.math-display{text-align:center;}
li p.indent { text-indent: 0em }
li p:first-child{ margin-top:0em; }
li p:last-child, li div:last-child { margin-bottom:0.5em; }
li p~ul:last-child, li p~ol:last-child{ margin-bottom:0.5em; }
.enumerate1 {list-style-type:decimal;}
.enumerate2 {list-style-type:lower-alpha;}
.enumerate3 {list-style-type:lower-roman;}
.enumerate4 {list-style-type:upper-alpha;}
div.newtheorem { margin-bottom: 2em; margin-top: 2em;}
.obeylines-h,.obeylines-v {white-space: nowrap; }
div.obeylines-v p { margin-top:0; margin-bottom:0; }
.overline{ text-decoration:overline; }
.overline img{ border-top: 1px solid black; }
td.displaylines {text-align:center; white-space:nowrap;}
.centerline {text-align:center;}
.rightline {text-align:right;}
pre.verbatim {font-family: monospace,monospace; text-align:left; clear:both; }
.fbox {padding-left:3.0pt; padding-right:3.0pt; text-indent:0pt; border:solid black 0.4pt; }
div.fbox {display:table}
div.center div.fbox {text-align:center; clear:both; padding-left:3.0pt; padding-right:3.0pt; text-indent:0pt; border:solid black 0.4pt; }
div.minipage{width:100%;}
div.center, div.center div.center {text-align: center; margin-left:1em; margin-right:1em;}
div.center div {text-align: left;}
div.flushright, div.flushright div.flushright {text-align: right;}
div.flushright div {text-align: left;}
div.flushleft {text-align: left;}
.underline{ text-decoration:underline; }
.underline img{ border-bottom: 1px solid black; margin-bottom:1pt; }
.framebox-c, .framebox-l, .framebox-r { padding-left:3.0pt; padding-right:3.0pt; text-indent:0pt; border:solid black 0.4pt; }
.framebox-c {text-align:center;}
.framebox-l {text-align:left;}
.framebox-r {text-align:right;}
span.thank-mark{ vertical-align: super }
span.footnote-mark sup.textsuperscript, span.footnote-mark a sup.textsuperscript{ font-size:80%; }
div.tabular, div.center div.tabular {text-align: center; margin-top:0.5em; margin-bottom:0.5em; }
table.tabular td p{margin-top:0em;}
table.tabular {margin-left: auto; margin-right: auto;}
td p:first-child{ margin-top:0em; }
td p:last-child{ margin-bottom:0em; }
div.td00{ margin-left:0pt; margin-right:0pt; }
div.td01{ margin-left:0pt; margin-right:5pt; }
div.td10{ margin-left:5pt; margin-right:0pt; }
div.td11{ margin-left:5pt; margin-right:5pt; }
table[rules] {border-left:solid black 0.4pt; border-right:solid black 0.4pt; }
td.td00{ padding-left:0pt; padding-right:0pt; }
td.td01{ padding-left:0pt; padding-right:5pt; }
td.td10{ padding-left:5pt; padding-right:0pt; }
td.td11{ padding-left:5pt; padding-right:5pt; }
table[rules] {border-left:solid black 0.4pt; border-right:solid black 0.4pt; }
.hline hr, .cline hr{ height : 0px; margin:0px; }
.hline td, .cline td{ padding: 0; }
.hline hr, .cline hr{border:none;border-top:1px solid black;}
.tabbing-right {text-align:right;}
div.float, div.figure {margin-left: auto; margin-right: auto;}
div.float img {text-align:center;}
div.figure img {text-align:center;}
.marginpar,.reversemarginpar {width:20%; float:right; text-align:left; margin-left:auto; margin-top:0.5em; font-size:85%; text-decoration:underline;}
.marginpar p,.reversemarginpar p{margin-top:0.4em; margin-bottom:0.4em;}
.reversemarginpar{float:left;}
table.equation {width:100%;}
.equation td{text-align:center; }
td.equation { margin-top:1em; margin-bottom:1em; }
td.equation-label { width:5%; text-align:center; }
td.eqnarray4 { width:5%; white-space: normal; }
td.eqnarray2 { width:5%; }
table.eqnarray-star, table.eqnarray {width:100%;}
div.eqnarray{text-align:center;}
div.array {text-align:center;}
div.pmatrix {text-align:center;}
table.pmatrix {width:100%;}
span.pmatrix img{vertical-align:middle;}
div.pmatrix {text-align:center;}
table.pmatrix {width:100%;}
span.bar-css {text-decoration:overline;}
img.cdots{vertical-align:middle;}
.partToc a, .partToc, .likepartToc a, .likepartToc {line-height: 200%; font-weight:bold; font-size:110%;}
.index-item, .index-subitem, .index-subsubitem {display:block}
div.caption {text-indent:-2em; margin-left:3em; margin-right:1em; text-align:left;}
div.caption span.id{font-weight: bold; white-space: nowrap; }
h1.partHead{text-align: center}
p.bibitem { text-indent: -2em; margin-left: 2em; margin-top:0.6em; margin-bottom:0.6em; }
p.bibitem-p { text-indent: 0em; margin-left: 2em; margin-top:0.6em; margin-bottom:0.6em; }
.paragraphHead, .likeparagraphHead { margin-top:2em; font-weight: bold;}
.subparagraphHead, .likesubparagraphHead { font-weight: bold;}
.verse{white-space:nowrap; margin-left:2em}
div.maketitle {text-align:center;}
h2.titleHead{text-align:center;}
div.maketitle{ margin-bottom: 2em; }
div.author, div.date {text-align:center;}
div.thanks{text-align:left; margin-left:10%; font-size:85%; font-style:italic; }
div.author{white-space: nowrap;}
div.abstract p {margin-left:5%; margin-right:5%;}
div.abstract {width:100%;}
.abstracttitle{text-align:center;margin-bottom:1em;}
.rotatebox{display: inline-block;}
/* end css.sty */
+333
View File
@@ -0,0 +1,333 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html >
<head><title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="generator" content="TeX4ht (https://tug.org/tex4ht/)">
<meta name="originator" content="TeX4ht (https://tug.org/tex4ht/)">
<!-- html -->
<meta name="src" content="oscillators.tex">
<link rel="stylesheet" type="text/css" href="oscillators.css">
</head><body
>
<h3 class="sectionHead"><span class="titlemark">1 </span> <a
id="x1-10001"></a>Oscillating Circuits</h3>
<!--l. 11--><p class="noindent" >A discussion of synthesizer oscillators requires an introduction to simple oscillating circuits. We
will discuss the most basic oscillating circuits, then we will move on to oscillators with
easily-tunable frequencies. Then we will tackle the most complex issue for the purposes of musical
synthesis: <span
class="ecti-1000">voltage control. </span>This is the important part! If we can control an oscillator&#8217;s frequency by
voltage, then we can make another circuit change its voltage, like a sequencer for example. Let&#8217;s
check it out!
<!--l. 17--><p class="noindent" >
<h4 class="subsectionHead"><span class="titlemark">1.1 </span> <a
id="x1-20001.1"></a>Passive Oscillators</h4>
<!--l. 19--><p class="noindent" >The simplest oscillators are those which rely on <span
class="ecti-1000">passive components</span>, electrical components which
do not generate power or &#8217;add amplitude&#8217; to a signal. These are components like resistors,
inductors, and capacitors which only dissipate, store, or release already-existing power introduced
by another component. An example of a non-passive component would be a power supply or a
transistor. Passive components tend to be governed by simpler rules that are easier to understand
and exploit.
<!--l. 25--><p class="indent" > The simplest oscillating circuit to my knowledge is the Resistor-Inductor-Capacitor or RLC
circuit. It&#8217;s not an &#8217;RIC&#8217; circuit because the letter I commonly represents current in electrical
engineering, so we use L to indicate inductors or inductance.
<!--l. 29--><p class="indent" > The diagram above depicts an RLC-circuit, with each component in series. We can come up
with an equation to describe its behavior, but first we need to know how each component responds
to voltage and current. <span
class="ecbx-1000">Resistors </span>are governed by <span
class="ecbx-1000">Ohm&#8217;s Law</span>:
<div class="math-display" >
<img
src="oscillators0x.png" alt="V = IR, I = V-,
R
" class="math-display" ></div>
<!--l. 33--><p class="nopar" > where <span
class="cmmi-10">R </span>is resistance, measured in Ohms (<span
class="cmr-10">&#x03A9;</span>). Resistors are called <span
class="ecbx-1000">linear components </span>because
their voltage-current response is linear i.e. an increase in voltage or current causes a linear increase
in the other. <span
class="ecbx-1000">Inductors </span>are governed by:
<div class="math-display" >
<img
src="oscillators1x.png" alt=" di 1 &#x222B;
V = Ldt, i = L V dt
" class="math-display" ></div>
<!--l. 39--><p class="nopar" > where <span
class="cmmi-10">L </span>is inductance, measured in Henries (H). Note that this means that the voltage across an
inductor responds to a change in current. If a current is constant, then the voltage vanishes. But if
we change the current, a voltage is generated across the inductor. That means if we send an
<span
class="ecti-1000">alternating current </span>which is always changing through the inductor, then we will get a
voltage across the inductor. This is unusual because an inductor is essentially just a
short-circuit and yet when a changing current passes through it, it will have a voltage like a
resistive element! You could say that inductors <span
class="ecti-1000">resist a change in current </span>in this sense of
resistance.
<!--l. 49--><p class="indent" > Finally, <span
class="ecbx-1000">Capacitors </span>are governed by the equation:
<div class="math-display" >
<img
src="oscillators2x.png" alt="i = C dv,
dt
" class="math-display" ></div>
<!--l. 50--><p class="nopar" > where <span
class="cmmi-10">C </span>is the capacitance of the component, measured in Farads (F). Here, the capacitor&#8217;s
behavior is similar but the relationship is sort of reversed or flipped, if you will. Now, if we have a
constant voltage across the capacitor then no current will flow. This makes sense because a
capacitor is essentially made from two conductive plates seperated from one another by a
non-conductive material. This is effectively a break in the circuit, as indicated by the standard
electrical symbol for a capacitor. But if we change the voltage across the capacitor, it starts to
conduct! changing the voltage somehow forces current to flow between the plates! It&#8217;s no mystery,
this is due to some complicated rules of physics known generally as <span
class="ecti-1000">electrodynamics</span>, but that&#8217;s for
another time.
<!--l. 59--><p class="indent" > Okay. We can connect these three equations mathematically by utilizing <span
class="ecbx-1000">Kirchoff&#8217;s Laws of</span>
<span
class="ecbx-1000">Voltage and Current</span>. This sounds a little complicated but it relies on some straightforward
principles. The core idea is that any current which enters a wire junction (usually called a <span
class="ecti-1000">node</span>)
must exit the junction in some way. For many reasons, another logical rule that follows from this is
that the voltages across each component in a loop must sum to zero. If this were not true, it would
result in charge pooling somewhere in the wire, which is almost always impossible. Here&#8217;s the
laws:
<ul class="itemize1">
<li class="itemize"><span
class="ecbx-1000">KIRCHOFF&#8217;S CURRENT LAW: </span>All current entering a node must exit i.e. the
sum of currents entering/leaving a node must always be <span
class="ecti-1000">zero.</span>
</li>
<li class="itemize"><span
class="ecbx-1000">KIRCHOFF&#8217;S VOLTAGE LAW: </span>The sum of component voltages over any loop
of wire must be zero.</li></ul>
<!--l. 71--><p class="indent" > If the current entering each node must also be leaving it, in our RLC circuit this means the
current through each node is the same. This is because the circuit is a closed loop! If the current
were not the same across each node, then it would have to pool somewhere or escape into thin air.
We can express this mathematically:
<div class="math-display" >
<img
src="oscillators3x.png" alt="iR + iL + iC = 0.
" class="math-display" ></div>
<!--l. 74--><p class="nopar" > We know the equations for the current through a resistor and capacitor:
<div class="math-display" >
<img
src="oscillators4x.png" alt="v dv
R-+ iL + C dt = 0.
" class="math-display" ></div>
<!--l. 76--><p class="nopar" > We know that voltage across an inductor is proportional to the change in current.
If we integrate with respect to time, we can get a figure for the current through an
inductor:
<div class="math-display" >
<img
src="oscillators5x.png" alt=" &#x222B;
iL =-1 vdt
L
" class="math-display" ></div>
<!--l. 79--><p class="nopar" >
<div class="math-display" >
<img
src="oscillators6x.png" alt=" &#x222B;
v-+ 1- v dt+ Cdv = 0.
R L dt
" class="math-display" ></div>
<!--l. 80--><p class="nopar" > Now we just differentiate:
<div class="math-display" >
<img
src="oscillators7x.png" alt=" d2v 1 dv 1
C --2 + ----+ --v = 0,
dt R dt L
" class="math-display" ></div>
<!--l. 82--><p class="nopar" > or
<div class="math-display" >
<img
src="oscillators8x.png" alt="av&#x2032;&#x2032;+ bv&#x2032; + cv = 0; a = C, b = 1-, c = 1-.
R L
" class="math-display" ></div>
<!--l. 83--><p class="nopar" > I would like to state here that the variable <span
class="cmmi-10">v </span>is a function dependent on time, so it should be
written as <span
class="cmmi-10">v</span><span
class="cmr-10">(</span><span
class="cmmi-10">t</span><span
class="cmr-10">) </span>for clarity. We prefer to be unclear here, because it is less cluttered to write
equations that way. Just keep this in mind.
<!--l. 87--><p class="indent" > What we have stumbled upon here is a truth that I find quite exciting but it has been the
nightmare of many underclassmen electrical engineers forced to learn this before finishing their
math coursework (who could I possibly be talking about here?): This circuit is governed by a
<span
class="ecbx-1000">homogenous second-order ordinary differential equation! </span>Unfortunately we cannot go over
the basics of ODE&#8217;s here. Like many stressed undergraduate engineers before you, you
will have to take my word as gospel. A homogenous second-order ODE is basically
an equation where the function (<span
class="cmmi-10">v </span>in this case) is not defined directly in terms of an
independent variable like time or <span
class="cmmi-10">x </span>or space etc, but in terms of its own differentials. This
equation is <span
class="ecti-1000">second-order </span>because the function is defined in terms of its second differential.
If you are very clever, you may already be thinking of how one might solve such an
equation to find <span
class="cmmi-10">v</span><span
class="cmr-10">(</span><span
class="cmmi-10">t</span><span
class="cmr-10">) </span>in terms of <span
class="cmmi-10">t </span>alone and not in terms of its differentials. If you are
even cleverer, you may be thinking about the classic function <span
class="cmmi-10">f</span><span
class="cmr-10">(</span><span
class="cmmi-10">t</span><span
class="cmr-10">) = </span><span
class="cmmi-10">e</span><sup><span
class="cmmi-7">x</span></sup><span
class="cmmi-10">, </span>because its
differential/integral is itself. If you are some kind of genius, you may even be considering also the
trigonometric functions (sine, cosine, not so much tangent here), because they have a similar
property. This will put you on the right track. Going forward, you will find that the
equations that govern these circuits involve many natural exponentials and sine waves
because of this property. Most ordinary differential equations are solved using complicated
techniques that involve relating differentials with natural exponentials and waves. I
cannot get into specifics here but if you are interested, I would keep this property in
mind.
<!--l. 105--><p class="indent" > OKAY. Back to the equation. Our equation can be represented by something called its
<span
class="ecti-1000">characteristic equation:</span>
<div class="math-display" >
<img
src="oscillators9x.png" alt="ar2 + br+ c = 0.
" class="math-display" ></div>
<!--l. 106--><p class="nopar" > This is just a representation of the equation that represents the order of each differential by a
power of the variable <span
class="ecti-1000">r</span>. If we solve it like a polynomial we get the quadratic formula:
<div class="math-display" >
<img
src="oscillators10x.png" alt=" &#x221A; -2-----
r1,2 = - b±--b---4ac.
2a
" class="math-display" ></div>
<!--l. 109--><p class="nopar" > You might realize that it is possible for the root of our characteristic equation to be complex
(<span
class="cmmi-10">r </span><span
class="cmr-10">= </span><span
class="cmmi-10">&#x03BB; </span><span
class="cmr-10">+ </span><span
class="cmmi-10">&#x03BC;i</span>). When this is the case, our solution is of the form:
<div class="math-display" >
<img
src="oscillators11x.png" alt="v(t) = C1e&#x03BB;tcos(&#x03BC;t)+ C2e&#x03BB;tsin(&#x03BC;t),
" class="math-display" ></div>
<!--l. 112--><p class="nopar" > where <span
class="cmmi-10">C</span><sub><span
class="cmr-7">1</span></sub><span
class="cmmi-10">,C</span><sub><span
class="cmr-7">2</span></sub> are constants determined by the <span
class="ecti-1000">initial conditions </span>of the system. Once all
the math is done, we will take a break to build some intuition for all this nonsense.
The important part is that the reason this is a solution has to do with the fact that
the differentials of exponentials and sine waves are equal to themselves or related to
themselves.
<!--l. 117--><p class="indent" > Now we can start getting specific with our constants. If we assume our system begins
with the components already charged (meaning we have allowed a current source to
keep a constant voltage across the components for enough time for the capacitor to
be charged to the positive voltage <span
class="cmmi-10">V</span> <sub><span
class="cmr-7">+</span></sub>), then we can say <span
class="cmmi-10">v</span><sub><span
class="cmr-7">0</span></sub> <span
class="cmr-10">= </span><span
class="cmmi-10">V</span> <sub><span
class="cmr-7">+</span></sub><span
class="cmmi-10">. </span>If it has sat for a
long long time, then there will be no change in voltage, so also we can say <span
class="cmmi-10">v</span><span
class="cmsy-10">&#x2032;</span><sub><span
class="cmr-7">0</span></sub> <span
class="cmr-10">= 0</span><span
class="cmmi-10">.</span>
So:
<div class="math-display" >
<img
src="oscillators12x.png" alt="v(0) = V+ = C1, v&#x2032;(0) = 0 = &#x03BB;C1 + &#x03BC;C2,
" class="math-display" ></div>
<!--l. 121--><p class="nopar" >
<div class="math-display" >
<img
src="oscillators13x.png" alt="C1 = V+, C2 = - &#x03BB;V+.
&#x03BC;
" class="math-display" ></div>
<!--l. 122--><p class="nopar" >
<!--l. 124--><p class="indent" > We can keep defining constants and it will quickly cause your mind to cloud over. The point
here is that if we charge up this circuit and then let it run, the voltage and current will oscillate
back and forth. I have included below a graph of a potential oscillation. The frequency of this
oscillation is determined by the constant <span
class="cmmi-10">&#x03BC;</span>, which is equal to the inverse of the root of
the product of inductance and capacitance (<span
class="cmmi-10">&#x03BC; </span><span
class="cmr-10">=</span> <img
src="oscillators14x.png" alt="--1-
&#x221A;LC--" class="frac" align="middle">). In electrical engineering, it is
usually called the <span
class="ecbx-1000">angular frequency </span>and is more commonly denoted with the greek
character <span
class="cmmi-10">&#x03C9;</span>. We found the constants <span
class="cmmi-10">C</span><sub><span
class="cmr-7">1</span></sub><span
class="cmmi-10">,C</span><sub><span
class="cmr-7">2</span></sub> to show that they are dependent on the initial
voltage we charge the circuit to, as well as the properties of the three components. We
have shown mathematically that the behavior of the circuit under certain parameters
(such that <span
class="cmmi-10">b</span><sup><span
class="cmr-7">2</span></sup> <span
class="cmmi-10">&#x003C; </span><span
class="cmr-10">4</span><span
class="cmmi-10">ac</span>) will be oscillatory in nature. But why does the circuit oscillate
sometimes?
<div class="center"
>
<!--l. 137--><p class="noindent" >
<!--l. 138--><p class="noindent" ><img
src="./img//natural-RLC.png" alt="PIC"
width="21" height="21" ></div>
<!--l. 141--><p class="indent" > Well, recall that an inductor generates a voltage once the current through it changes, and that
a capacitor begins conducting current once the voltage across it changes. When we charge the
capacitor to some voltage and then close the circuit, the voltage across the capacitor suddenly
becomes the voltage across the resistor as well. When there is a voltage across a resistor which is
connected in a loop, then a current must flow. Conversely, the moment the circuit is closed it
forces the voltage to drop because a current must flow through the resistor. If the capacitor
were alone in series with the resistor, then it would simply discharge to a voltage of
0.
<!--l. 148--><p class="indent" > As the capacitor pushes current through the inductor, the inductor begins to respond. Initially
it acts as a short, but as the current through it changes, it begins to generate a voltage. That
voltage causes the inductor to push current into the capacitor again, charging it. Then once the
inductor has discharged its stored energy, the capacitor is recharged and it begins to conduct
again. This continues until all of the electrical energy is dissipated through the resistor (and
realistically also through the resistances in the capacitor and inductor) as heat until there is none
left. Kind of cool!
<!--l. 154--><p class="indent" > If we tuned the capacitance and inductance properly, we could get one of these circuits to
oscillate at an audible frequency. This would not be an ideal circuit for music making, and there
are two big reasons for this:
<ul class="itemize1">
<li class="itemize">The circuit will only oscillate for a short period after it is triggered, preventing us from
ever using it to play any sustained note.
</li>
<li class="itemize">YOU CAN&#8217;T TUNE THE FREQUENCY! You would need a variable inductor or
transformer, and those solutions quickly become impractical. Unless you like plucky
drone music, you&#8217;re out of luck here.</li></ul>
<!--l. 164--><p class="indent" > I&#8217;m sure there are many other kinds of passive oscillating circuits, but I think we have done
enough here. Next we will consider <span
class="ecti-1000">active oscillators.</span>
<!--l. 169--><p class="noindent" >
<h4 class="subsectionHead"><span class="titlemark">1.2 </span> <a
id="x1-30001.2"></a>Active Oscillators</h4>
</body></html>
+22
View File
@@ -0,0 +1,22 @@
<title>PROJECTS</title>
<body>
<div id="body">
<h1>PROJECTS</h1>
<p>
Here you will find documentation of some of the things I've made (code, circuits, machines, etc):
</p>
<h3>SOFTWARE</h3>
<ul>
<li><a href="index.php?type=article&name=mypage">This website!</a></li>
<li><a href="index.php?type=article&name=mlib-c">Writing a machine learning library in C</a></li>
<li><a href="index.php?type=article&name=starsky">Night sky generator</a></li>
<li><a href="">Progressive Steps to Analog ANNs (coming soon!)</a></li>
<li><a href=""></a></li>
</ul>
<h3>HARDWARE</h3>
<ul>
<li><a href="index.php?type=article&name=modular-synth">Building a modular synthesizer</a></li>
<li><a href="index.php?type=article&name=6502">Building a 6502-based computer with terminal graphics processing</a></li>
</ul>
</div>
</body>
+63
View File
@@ -0,0 +1,63 @@
<title>NIGHT SKY</title>
<body>
<div id="body">
<h1>NIGHT SKY GENERATION</h1>
<p>
This is just a small program I wrote in Python to generate a starry night sky. It uses Pillow for image processing,
and it was a fun exercise in basic random image generation. I wanted to make a program that you could run over
and over and it could always spit out a pretty picture that was completely unique each time. Naturally, the program
employs a lot of random number generation using NumPy as well as some basic linear math for galaxies. The largest
challenge is trying to get the stars to look somewhat natural in color and shape, and that's where I think the
program needs most development. For now however, I think the results are acceptable.
</p>
<p>
I came up with the idea during development of this website. I needed a pretty night sky backround for my homepage,
but I didn't want to just use some stock image and I don't have any graphic design experience, so I just wrote a
script for it in Python. I wanted it to look like a night sky if it was unobstructed by the atmosphere and light
pollution; where the milky way and all the stars are highly visible. I also really wanted to have a program that could
use random number generation to build a really pretty picture every time without the need for human tweaking, so you
could just run the code and get a unique and beautiful picture.
</p>
<p>
The first thing I did was get Pillow (an image-processing library for Python) as well as NumPy (a math library) for its
many random number generators, and I started drawing little yellow circles at random locations with random sizes. This
made a picture, but not really anything close ot a night sky yet. The real night sky has stars distributed in clusters
broken by empy space. It also has a big streak of stars and gasses across the night sky where the Milky Way passes through
our sky. Finally, stars are not just yellow, they're usually closer to blue or white with variation based off of temperature.
I shelved the coloring issure and went on to create a galaxy system. I wrote a function which takes an x-value,
a slope, and an intercept, and spits out a y value corresponding to the linear equation defined by those parameters.
This line will be the center of the galaxy which the stars will cluster around.
</p>
<p>
To actually get the stars clustered around the line, I used a normal distribution random number generator to randomize
the distance of each star from the center line, with an average distance to make the distribution appear more natural.
This produces a nice line distribution , but doing this only once leaves the 'galaxy' looking kind of weak. So I draw stars
around the line multiple times with different normal distributions to produce a layered sort of look, which I think makes it look
much more natural. Finally, I just kept my initial code to generate stars at random locations to build a background
behind the galaxy.
</p>
<p>
Next it was time to tackle the coloration. Initially I just tried generating each star with a random RGB value and tried
restricting the range of the R, G, or B in order to keep the stars within the range of reality (no green or purple stars, etc).
This didn't really work very well. I realized that stars' colors are determined by their temperature and there is a direct
correlation between a star's color and temperature in kelvin. So after doing a little research, I found a Python script that
does just this! It converts degrees kelvin into an RGB value. All I had to do was give each star a random temperature based
on the range of actual star temperatures, and then I could get rid of all impossible RGB values for stars. This works well, although
there are still too many red stars which are generally pretty uncommon in the night sky. I think it actually makes the image look
more interesting from an aesthetic perspective, even if it's not quite accurate.
</p>
<p>
The last thing I've done was add star clusters, in addition to the galaxy system. This one is pretty simple. I just
pick a random set of points and generate stars at a random distance. It leaves the image looking a lot more realistic
and beautiful.
</p>
<p>
That is as far as the script goes for now. I have several issues with it I want to improve in the future. One glaring
issue is that Python is very slow, and generating high-resolution images can take several seconds. I'd like to rewrite
the program in a more memory-efficient langauge like C++ to reduce processing time. Another issure is that the actual individual stars are pretty
rudimentary. They're just circles with a blur effect. I'd like to introduce some kind of flare to them to make them more
interesting. I would also like to include other types of astronomical bodies like comets, nebulae, planets, etc. I think
the program as it exists is still compelling and I am happy with the images it produces. That's all!
</p>
</div>
</body>
+26
View File
@@ -0,0 +1,26 @@
<title>WRITINGS</title>
<body>
<div id="body">
<h1>PERSONAL WRITINGS</h1>
<p>
Here you can find some of the things I've written, as PDF documents. There are some essays and speeches I've written up, mostly.
Take a look!
</p>
<ul>
<li><a href="writings/onitall.pdf">"Where is My Democracy?"</a>
Speech I gave at a local art collective in town, about democracy and freedom and
what to do about all this nonsense.</li>
<li><a href="writings/on-class-war.pdf">On Class War</a>: Short essay providing an introduction to basic Marxist
concepts of economic class, in order to
establish the idea that there are internal class antagonisms that result in such a thing as a 'class war.'
The article was published in the IWW worker's paper <a href="https://sonhuelgaz.org/2024/09/24/what-is-class-war-part-1/">
sonhuelgaz.org</a>.</li>
<li><a href="writings/sept24-police-aggression.pdf">Short report</a> on a homeless eviction that took place at my hometown.
I wrote the report to help inform residents about the way my local police force speaks about the poor folks they are paid
to serve. Just a summary, play-by-play of events, and a little conclusion. It IS a partisan article, I don't lie but I
don't hide my views either. I think any journalist who pretends to have no perspective is being dishonest.
I believe in a form of <i>movement journalism</i> that does not present a pastiche of "objectivity," but instead seeks
to use truth and a perspective to build a better world.</li>
</ul>
</div>
</body>
+6
View File
@@ -0,0 +1,6 @@
<h1>|= CONTACT</h1>
<p>Please reach out to me anytime and for any reason!
The best place to reach me is by my email:
</p>
<ul><li><a href='mailto:me@jameswitzeman.net'>me@jameswitzeman.net</a></li></ul>
+13
View File
@@ -0,0 +1,13 @@
<h1>|= CHECK IT OUT!</h1>
<p>
Welcome to my homepage. This website is a place to look
at my cool things and ideas. You can also contact me!
I am a programmer, musician, and other things also.
If your computer isn't working, <i>I can probably
help you!</i> if you or someone you love wants to learn
to play piano or needs to pass college algebra, <i> I am
your guy!</i> If you want a friend, <i> definitely email me!</i>
</p>
<!--include content/about.html-->
+47
View File
@@ -0,0 +1,47 @@
<h1>SERVICES</h1>
<h2>|= IT SOLUTIONS</h2>
<p>It sounds really fancy phrased that way.
If you need someone to maintain your website, fix
your internet, write you some kind of program, or even
fix up a broken/old PC, I'm your guy!
</p>
<p>
My skillset is somewhat broad. I can do basic electronics
repairs, I was trained in logic design in school. I know
a lot of transistor math. I have experience programming in
C, PHP, JavaScript, Python, CSS, etc. I can do pretty good
web dev and also know how to work with Docker well. Here
at home I have a nice media server full of **legally obtained**
movies and shows. If you have a nasty old or cheap PC and want
help getting it running better, I can speed it up. The secret
is running Linux! Some things I can do:
</p>
<ul>
<li>Hardware repair</li>
<li>Website design and maintenance</li>
<li>Speed up slow, old PCs </li>
<li>Home networking</li>
<li>Programming help in Python, C, PHP, JavaScript</li>
</ul>
<h2>|= MATH TUTORING</h2>
<p>
I am a trained computer engineer and know a lot of
wicked maths. I can most likely help tutor you in any
math up to Calculus III. Some classes I passed:
</p>
<ul>
<li>Algebra I, II</li>
<li>Pre-Calculus</li>
<li>AP Calculus A,B,C</li>
<li>Calculus I</li>
<li>Calculus II</li>
<li>Multivariable Calc/Calc III</li>
</ul>
<h2>|= PIANO LESSONS</h2>
<p>I am also a proficient pianist! If you're trying to
learn, or have a child looking to pick up music, I can
help any beginner to intermediate player!</p>
+167
View File
@@ -0,0 +1,167 @@
/* Common CSS */
html {
background-color: var(--bg-primary);
color: var(--grey);
}
h1, h2 {
font-size: 1em;
}
body {
margin: 0;
font-family: "Liberation Mono", monospace;
}
#titlebar {
font-family: unifont;
}
#titlebar h1 {
font-size: 2rem;
}
#subhead {
color: var(--cyan);
font-weight: bold;
}
#subhead a {
text-decoration: none;
padding: 0 0.5em 0 0.5em;
margin: 0 0.5em 0 0.5em;
color: var(--cyan);
}
#subhead a:hover {
/*text-decoration: underline;*/
background-color: var(--bg-secondary);
color: var(--orange);
}
#subhead ul {
border-bottom: 2px dashed var(--red);
}
.content p {
text-indent: 2em;
}
#foot {
border-top: 4px double var(--yellow);
padding-top: 2em;
padding-bottom: 1em;
display: flex;
align-items: center;
justify-content: center;
}
a {
color: var(--cyan);
}
a:hover {
color: var(--orange);
text-decoration: none;
cursor: pointer;
}
/* Desktop CSS */
@media only screen and (min-aspect-ratio: 0.72) {
body {
display: flex;
align-items: stretch;
flex-direction: column;
width: 100vw;
}
#head {
display: flex;
flex-direction: column;
align-items: center;
}
#titlebar {
align-self: stretch;
border-bottom: 4px double var(--yellow);
display: flex;
align-items: center;
justify-content: space-between;
height: 2em;
}
#titlebar h1{
margin: 0 0 0 0.3em;
}
#titlebar .item1 {
}
#titlebar .item2 {
}
#subhead {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
#subhead ul {
list-style-type: none;
display: flex;
margin: 0 0 0 0;
}
#subhead ul li {
}
#motd {
margin-left: 10px;
tab-size: 4;
white-space: pre-wrap;
}
.content {
width: 90vw;
max-width: 65em;
margin-top: 1em;
padding-left: 1em;
/*align-self: center;*/
margin-left: 2em;
min-height: 85vh;
border-left: 2px solid var(--bg-secondary);
}
}
/* Mobile CSS */
@media screen and (max-aspect-ratio: 0.71) {
html {
font-size: 2vh;
}
#titlebar {
border-bottom: 4px double var(--yellow);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
#titlebar h1{
margin: 0;
padding-bottom: 0.3em;
}
#titlebar .item1 {
}
#titlebar .item2 {
}
#subhead {
color: var(--cyan);
display: flex;
flex-direction: column-reverse;
justify-content: space-between;
align-items: stretch;
width: 100%;
}
#subhead ul {
list-style-type: none;
display: flex;
justify-content: center;
margin: 0 0 0 0;
}
#motd {
margin-left: 0.5em;
display: flex;
justify-content: left;
align-items: center;
tab-size: 4;
white-space: pre-wrap;
}
.content {
margin-left: 15px;
font-size: 0.7em;
}
}
+38
View File
@@ -0,0 +1,38 @@
/* Color variables */
:root {
/* Ayu Dark */
/* --bg-primary: #0A0E14;
--red: #FF3333;
--red2: #FF3333;
--green: #C2D94C;
--green2: #C2D94C;
--orange: #FF8F40;
--orange2: #FF8F40;
--blue: #59C2FF;
--blue2: #59C2FF;
--yellow: #FFEE99;
--yellow2: #FFEE99;
--cyan: #95E6CB;
--cyan2: #95E6CB;
--grey: #B3B1AD;
--grey2: #B3B1AD;
--bg-secondary: #4D5566;
*/
/* Elementary */
--bg-primary: #303030;
--red: #E1321A;
--red2: #FF361E;
--green: #6AB017;
--green2: #7BC91F;
--orange: #FFC005;
--orange2: #FFD00A;
--blue: #004F9E;
--blue2: #0071FF;
--yellow: #EC0048;
--yellow2: #FF1D62;
--cyan: #2AA7E7;
--cyan2: #4BB8FD;
--grey: #F2F2F2;
--grey2: #A020f0;
--bg-secondary: #4D5566;
}
+17
View File
@@ -0,0 +1,17 @@
/* Fonts */
@font-face {
font-family: hackmono;
src: url(fonts/Hack-Regular.ttf);
}
@font-face {
font-family: "Code New Roman";
src: url("fonts/Code New Roman.woff");
}
@font-face {
font-family: unifont;
src: url(fonts/unifont-17.0.04.otf);
}
@font-face {
font-family: "Liberation Mono";
src: url(/fonts/liberation-mono.regular.ttf);
}
+8
View File
@@ -0,0 +1,8 @@
/* Desktop CSS */
@media only screen and (min-aspect-ratio: 0.72) {
}
/* Mobile CSS */
@media screen and (max-aspect-ratio: 0.71) {
}
Executable
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- Accept terminus font -->
<selectfont>
<acceptfont>
<pattern>
<patelt name="family"><string>Terminus</string></patelt>
</pattern>
</acceptfont>
</selectfont>
</fontconfig>
+1
View File
@@ -0,0 +1 @@
Dimitar Zhekov <dimitar.zhekov@gmail.com>
+112
View File
@@ -0,0 +1,112 @@
Version 4.49.1:
* Fixed install-otb and uninstall-otb to use otbdir instead of x11dir.
Version 4.49:
* Added Open Type Bitmap support.
* Altered ascii grave in some sizes to be more useful as a back quote.
* Fixed 21B5, added 21B2 and 21B3.
Version 4.48:
* Added the basic 27 hebrew letters and sheqel, with uppercase height.
* Some improvements in the font build scripts.
Version 4.47:
* Added 35 new characters (33 glyphs).
* Replaced ao2-variant "ae" with ao1 "ae", it was too similar to "oe".
* Some fixes and improvements (17 characters in various sizes/styles).
* Significantly improved the font conversion tools. Python 3.5.0 or
Node 6.9.0 are now required to build the font.
Version 4.46:
* The X11 8-bit code pages are not installed by default.
* Added IBM-437 8-bit code page for X11.
* The CRT VGA weight for Linux console is not installed by default.
* Removed the Linux console mapping files.
They should be provided by the console packages.
* Removed the BSD console installation.
The recent BSD-s have a new console subsystem.
* Added 50 new characters. Mostly math, but also Buglarian yat and yus.
* Rewritten the font conversion tools in python and javascript.
The full unicode range (17x64K) is now supported.
* The Windows installer can be built from sources.
* Small fixes and improvements (7 characters in various sizes).
* Renamed (un)install-ref to (un)install-psf-ref.
Version 4.40:
* Added 6 combining accents as separate characters.
* Added 14 letters with dot above / dot below.
* Added partial subscript and superscript: all digits and 11 letters.
* Added 30+ math characters, notably large braces, brackets and parens.
* Added unicode range 2800-28FF in two variants (br1 and br2).
* A few small character fixes.
* Altered configure to be a bit more POSIX compliant.
* Replaced some obscure (un)install Makefile targets with variables.
Version 4.39:
* Added ballot, checkmark, heavy ballot and heavy checkmark.
* Changed HT, LF etc. in sizes 14 and 18-hi2 to be proportional to the
letter height, not the matrix height.
* Added the powerline characters E0A0..E0A2 and E0B0..E0B3.
* Added diameter (2300) - same gluph as empty set (2205).
* Small improvements in size 32.
Version 4.38:
* Added 23 pseudographic characters, most notably rounded frames.
* Added new td1 variant with centered ascii tidle.
* Fixed Y acute in sizes 22 and 28, internal optimizations (invisible).
Version 4.36:
* Removed the rarely used cm2 variant.
* Added new ll2 variant with more distinctive l.
* Added quotereversed (201B), quotedblreversed (201F), I/i/U/u dotbelow
(1ECA, 1ECB, 1EE4, 1EE5).
* Moved all quotes and alike in size 32 one line down.
* Small fixes and improvements (t commaaccent, quotes alignment etc. in
some sizes).
* Windows installation: creates the relevant registry key.
Version 4.35:
* Added hi2 variant for size 18.
* Fixes in size 18: normal V, normal W, bold X, H stroke, some
pseudographic characters and the *NIX installation.
Version 4.34:
* Added size 10x18.
* Small fixes and improvements in section, multiply, Eng/eng, Dje,
dje, house, male, zeta and various characters in the different sizes.
* The default cyrillic ghe is now ge1, with ge2 available as an option.
Version 4.32:
* Slightly more distintive normal M and W.
* Rounded 28-bold, 32-normal, 32-bold.
Also removed the 32-normal kx2 style.
* Small changes: Che/che stroke, phi, Zhe/zhe etc.
* Linux console: the default bold is now framebuffer.
* Smaller Makefile, GNU make required.
* Changed the font license to SIL OFL 1.1.
It's FSF approved, no need to worry.
* And, as you can see, Terminus Font is now on sourceforge.
Version 4.30:
* Added size 22 (not very good).
* Added another 25 characters.
* Various small fixes and improvements.
* Changed the default prefix and x11dir.
Version 4.28:
* Heavy frames (written mostly by Tim Allen) and a few more letters.
* Altered trianges and arrows, small bugfixes.
* Reorganized the 512-character console font to include more letters
instead of the IBM-437 specific pseudographics.
+1
View File
@@ -0,0 +1 @@
See OFL.TXT
+1
View File
@@ -0,0 +1 @@
See README
+315
View File
@@ -0,0 +1,315 @@
INT = python3
EXT = py
BIN = ./bin
UCS2ANY = $(INT) $(BIN)/ucstoany.$(EXT)
BDF2PSF = $(INT) $(BIN)/bdftopsf.$(EXT)
UCS2X11 = $(INT) $(BIN)/ucstoany.$(EXT) -f
BDF2PCF = bdftopcf
#BDF2OTB = fontforge -lang=ff -c 'Open($$3); ScaleToEm(1024); Generate($$2)'
BDF2OTB = $(INT) $(BIN)/otb1cli.$(EXT)
REG_8859_1 = ISO8859 1
REG_8859_2 = ISO8859 2
REG_8859_5 = ISO8859 5
REG_8859_7 = ISO8859 7
REG_8859_9 = ISO8859 9
REG_MS_1251 = Microsoft CP1251
REG_8859_13 = ISO8859 13
REG_8859_15 = ISO8859 15
REG_8859_16 = ISO8859 16
REG_MS_1255 = Microsoft CP1255
REG_IBM_437 = IBM CP437
REG_KOI8_R = KOI8 R
REG_KOI8_U = KOI8 U
REG_BG_MIK = Bulgarian MIK
REG_PT_154 = Paratype PT154
REG_XOS4_2 = XOS4 2
REG_10646_1 = ISO10646 1
PSF_8859_1 = ter-112n.psf ter-114n.psf ter-114b.psf ter-116n.psf ter-116b.psf ter-118n.psf ter-118b.psf ter-120n.psf ter-120b.psf ter-122n.psf ter-122b.psf ter-124n.psf ter-124b.psf ter-128n.psf ter-128b.psf ter-132n.psf ter-132b.psf
PSF_8859_2 = ter-212n.psf ter-214n.psf ter-214b.psf ter-216n.psf ter-216b.psf ter-218n.psf ter-218b.psf ter-220n.psf ter-220b.psf ter-222n.psf ter-222b.psf ter-224n.psf ter-224b.psf ter-228n.psf ter-228b.psf ter-232n.psf ter-232b.psf
PSF_8859_7 = ter-712n.psf ter-714n.psf ter-714b.psf ter-716n.psf ter-716b.psf ter-718n.psf ter-718b.psf ter-720n.psf ter-720b.psf ter-722n.psf ter-722b.psf ter-724n.psf ter-724b.psf ter-728n.psf ter-728b.psf ter-732n.psf ter-732b.psf
PSF_8859_9 = ter-912n.psf ter-914n.psf ter-914b.psf ter-916n.psf ter-916b.psf ter-918n.psf ter-918b.psf ter-920n.psf ter-920b.psf ter-922n.psf ter-922b.psf ter-924n.psf ter-924b.psf ter-928n.psf ter-928b.psf ter-932n.psf ter-932b.psf
PSF_MS_1251 = ter-c12n.psf ter-c14n.psf ter-c14b.psf ter-c16n.psf ter-c16b.psf ter-c18n.psf ter-c18b.psf ter-c20n.psf ter-c20b.psf ter-c22n.psf ter-c22b.psf ter-c24n.psf ter-c24b.psf ter-c28n.psf ter-c28b.psf ter-c32n.psf ter-c32b.psf
PSF_8859_13 = ter-d12n.psf ter-d14n.psf ter-d14b.psf ter-d16n.psf ter-d16b.psf ter-d18n.psf ter-d18b.psf ter-d20n.psf ter-d20b.psf ter-d22n.psf ter-d22b.psf ter-d24n.psf ter-d24b.psf ter-d28n.psf ter-d28b.psf ter-d32n.psf ter-d32b.psf
PSF_8859_16 = ter-g12n.psf ter-g14n.psf ter-g14b.psf ter-g16n.psf ter-g16b.psf ter-g18n.psf ter-g18b.psf ter-g20n.psf ter-g20b.psf ter-g22n.psf ter-g22b.psf ter-g24n.psf ter-g24b.psf ter-g28n.psf ter-g28b.psf ter-g32n.psf ter-g32b.psf
PSF_MS_1255 = ter-h12n.psf ter-h14n.psf ter-h14b.psf ter-h16n.psf ter-h16b.psf ter-h18n.psf ter-h18b.psf ter-h20n.psf ter-h20b.psf ter-h22n.psf ter-h22b.psf ter-h24n.psf ter-h24b.psf ter-h28n.psf ter-h28b.psf ter-h32n.psf ter-h32b.psf
PSF_IBM_437 = ter-i12n.psf ter-i14n.psf ter-i14b.psf ter-i16n.psf ter-i16b.psf ter-i18n.psf ter-i18b.psf ter-i20n.psf ter-i20b.psf ter-i22n.psf ter-i22b.psf ter-i24n.psf ter-i24b.psf ter-i28n.psf ter-i28b.psf ter-i32n.psf ter-i32b.psf
PSF_KOI8_RV = ter-k14n.psf ter-k14b.psf ter-k16n.psf ter-k16b.psf
PSF_KOI8_R = ter-k12n.psf ter-k18n.psf ter-k18b.psf ter-k20n.psf ter-k20b.psf ter-k22n.psf ter-k22b.psf ter-k24n.psf ter-k24b.psf ter-k28n.psf ter-k28b.psf ter-k32n.psf ter-k32b.psf
PSF_BG_MIK = ter-m12n.psf ter-m14n.psf ter-m14b.psf ter-m16n.psf ter-m16b.psf ter-m18n.psf ter-m18b.psf ter-m20n.psf ter-m20b.psf ter-m22n.psf ter-m22b.psf ter-m24n.psf ter-m24b.psf ter-m28n.psf ter-m28b.psf ter-m32n.psf ter-m32b.psf
PSF_PT_154 = ter-p12n.psf ter-p14n.psf ter-p14b.psf ter-p16n.psf ter-p16b.psf ter-p18n.psf ter-p18b.psf ter-p20n.psf ter-p20b.psf ter-p22n.psf ter-p22b.psf ter-p24n.psf ter-p24b.psf ter-p28n.psf ter-p28b.psf ter-p32n.psf ter-p32b.psf
PSF_KOI8_UV = ter-u14n.psf ter-u14b.psf ter-u16n.psf ter-u16b.psf
PSF_KOI8_U = ter-u12n.psf ter-u18n.psf ter-u18b.psf ter-u20n.psf ter-u20b.psf ter-u22n.psf ter-u22b.psf ter-u24n.psf ter-u24b.psf ter-u28n.psf ter-u28b.psf ter-u32n.psf ter-u32b.psf
PSF_XOS4_2 = ter-v12n.psf ter-v14n.psf ter-v14b.psf ter-v16n.psf ter-v16b.psf ter-v18n.psf ter-v18b.psf ter-v20n.psf ter-v20b.psf ter-v22n.psf ter-v22b.psf ter-v24n.psf ter-v24b.psf ter-v28n.psf ter-v28b.psf ter-v32n.psf ter-v32b.psf
PSF = $(PSF_8859_1) $(PSF_8859_2) $(PSF_8859_7) $(PSF_8859_9) $(PSF_MS_1251) $(PSF_8859_13) $(PSF_8859_16) $(PSF_MS_1255) $(PSF_IBM_437) $(PSF_KOI8_RV) $(PSF_KOI8_R) $(PSF_BG_MIK) $(PSF_PT_154) $(PSF_KOI8_UV) $(PSF_KOI8_U) $(PSF_XOS4_2)
PSF_VGAW_8859_1 = ter-114v.psf ter-116v.psf
PSF_VGAW_8859_2 = ter-214v.psf ter-216v.psf
PSF_VGAW_8859_7 = ter-714v.psf ter-716v.psf
PSF_VGAW_8859_9 = ter-914v.psf ter-916v.psf
PSF_VGAW_MS_1251 = ter-c14v.psf ter-c16v.psf
PSF_VGAW_8859_13 = ter-d14v.psf ter-d16v.psf
PSF_VGAW_8859_16 = ter-g14v.psf ter-g16v.psf
PSF_VGAW_MS_1255 = ter-h14v.psf ter-h16v.psf
PSF_VGAW_IBM_437 = ter-i14v.psf ter-i16v.psf
PSF_VGAW_KOI8_RV = ter-k14v.psf ter-k16v.psf
PSF_VGAW_BG_MIK = ter-m14v.psf ter-m16v.psf
PSF_VGAW_PT_154 = ter-p14v.psf ter-p16v.psf
PSF_VGAW_KOI8_UV = ter-u14v.psf ter-u16v.psf
PSF_VGAW_XOS4_2 = ter-v14v.psf ter-v16v.psf
PSF_VGAW = $(PSF_VGAW_8859_1) $(PSF_VGAW_8859_2) $(PSF_VGAW_8859_7) $(PSF_VGAW_8859_9) $(PSF_VGAW_MS_1251) $(PSF_VGAW_8859_13) $(PSF_VGAW_8859_16) $(PSF_VGAW_MS_1255) $(PSF_VGAW_IBM_437) $(PSF_VGAW_KOI8_RV) $(PSF_VGAW_BG_MIK) $(PSF_VGAW_PT_154) $(PSF_VGAW_KOI8_UV) $(PSF_VGAW_XOS4_2)
PCF_8859_1 = ter-112n.pcf ter-112b.pcf ter-114n.pcf ter-114b.pcf ter-116n.pcf ter-116b.pcf ter-118n.pcf ter-118b.pcf ter-120n.pcf ter-120b.pcf ter-122n.pcf ter-122b.pcf ter-124n.pcf ter-124b.pcf ter-128n.pcf ter-128b.pcf ter-132n.pcf ter-132b.pcf
PCF_8859_2 = ter-212n.pcf ter-212b.pcf ter-214n.pcf ter-214b.pcf ter-216n.pcf ter-216b.pcf ter-218n.pcf ter-218b.pcf ter-220n.pcf ter-220b.pcf ter-222n.pcf ter-222b.pcf ter-224n.pcf ter-224b.pcf ter-228n.pcf ter-228b.pcf ter-232n.pcf ter-232b.pcf
PCF_8859_5 = ter-512n.pcf ter-512b.pcf ter-514n.pcf ter-514b.pcf ter-516n.pcf ter-516b.pcf ter-518n.pcf ter-518b.pcf ter-520n.pcf ter-520b.pcf ter-522n.pcf ter-522b.pcf ter-524n.pcf ter-524b.pcf ter-528n.pcf ter-528b.pcf ter-532n.pcf ter-532b.pcf
PCF_8859_7 = ter-712n.pcf ter-712b.pcf ter-714n.pcf ter-714b.pcf ter-716n.pcf ter-716b.pcf ter-718n.pcf ter-718b.pcf ter-720n.pcf ter-720b.pcf ter-722n.pcf ter-722b.pcf ter-724n.pcf ter-724b.pcf ter-728n.pcf ter-728b.pcf ter-732n.pcf ter-732b.pcf
PCF_8859_9 = ter-912n.pcf ter-912b.pcf ter-914n.pcf ter-914b.pcf ter-916n.pcf ter-916b.pcf ter-918n.pcf ter-918b.pcf ter-920n.pcf ter-920b.pcf ter-922n.pcf ter-922b.pcf ter-924n.pcf ter-924b.pcf ter-928n.pcf ter-928b.pcf ter-932n.pcf ter-932b.pcf
PCF_MS_1251 = ter-c12n.pcf ter-c12b.pcf ter-c14n.pcf ter-c14b.pcf ter-c16n.pcf ter-c16b.pcf ter-c18n.pcf ter-c18b.pcf ter-c20n.pcf ter-c20b.pcf ter-c22n.pcf ter-c22b.pcf ter-c24n.pcf ter-c24b.pcf ter-c28n.pcf ter-c28b.pcf ter-c32n.pcf ter-c32b.pcf
PCF_8859_13 = ter-d12n.pcf ter-d12b.pcf ter-d14n.pcf ter-d14b.pcf ter-d16n.pcf ter-d16b.pcf ter-d18n.pcf ter-d18b.pcf ter-d20n.pcf ter-d20b.pcf ter-d22n.pcf ter-d22b.pcf ter-d24n.pcf ter-d24b.pcf ter-d28n.pcf ter-d28b.pcf ter-d32n.pcf ter-d32b.pcf
PCF_8859_15 = ter-f12n.pcf ter-f12b.pcf ter-f14n.pcf ter-f14b.pcf ter-f16n.pcf ter-f16b.pcf ter-f18n.pcf ter-f18b.pcf ter-f20n.pcf ter-f20b.pcf ter-f22n.pcf ter-f22b.pcf ter-f24n.pcf ter-f24b.pcf ter-f28n.pcf ter-f28b.pcf ter-f32n.pcf ter-f32b.pcf
PCF_8859_16 = ter-g12n.pcf ter-g12b.pcf ter-g14n.pcf ter-g14b.pcf ter-g16n.pcf ter-g16b.pcf ter-g18n.pcf ter-g18b.pcf ter-g20n.pcf ter-g20b.pcf ter-g22n.pcf ter-g22b.pcf ter-g24n.pcf ter-g24b.pcf ter-g28n.pcf ter-g28b.pcf ter-g32n.pcf ter-g32b.pcf
PCF_IBM_437 = ter-i12n.pcf ter-i12b.pcf ter-i14n.pcf ter-i14b.pcf ter-i16n.pcf ter-i16b.pcf ter-i18n.pcf ter-i18b.pcf ter-i20n.pcf ter-i20b.pcf ter-i22n.pcf ter-i22b.pcf ter-i24n.pcf ter-i24b.pcf ter-i28n.pcf ter-i28b.pcf ter-i32n.pcf ter-i32b.pcf
PCF_KOI8_R = ter-k12n.pcf ter-k12b.pcf ter-k14n.pcf ter-k14b.pcf ter-k16n.pcf ter-k16b.pcf ter-k18n.pcf ter-k18b.pcf ter-k20n.pcf ter-k20b.pcf ter-k22n.pcf ter-k22b.pcf ter-k24n.pcf ter-k24b.pcf ter-k28n.pcf ter-k28b.pcf ter-k32n.pcf ter-k32b.pcf
PCF_PT_154 = ter-p12n.pcf ter-p12b.pcf ter-p14n.pcf ter-p14b.pcf ter-p16n.pcf ter-p16b.pcf ter-p18n.pcf ter-p18b.pcf ter-p20n.pcf ter-p20b.pcf ter-p22n.pcf ter-p22b.pcf ter-p24n.pcf ter-p24b.pcf ter-p28n.pcf ter-p28b.pcf ter-p32n.pcf ter-p32b.pcf
PCF_KOI8_U = ter-u12n.pcf ter-u12b.pcf ter-u14n.pcf ter-u14b.pcf ter-u16n.pcf ter-u16b.pcf ter-u18n.pcf ter-u18b.pcf ter-u20n.pcf ter-u20b.pcf ter-u22n.pcf ter-u22b.pcf ter-u24n.pcf ter-u24b.pcf ter-u28n.pcf ter-u28b.pcf ter-u32n.pcf ter-u32b.pcf
PCF_10646_1 = ter-x12n.pcf ter-x12b.pcf ter-x14n.pcf ter-x14b.pcf ter-x16n.pcf ter-x16b.pcf ter-x18n.pcf ter-x18b.pcf ter-x20n.pcf ter-x20b.pcf ter-x22n.pcf ter-x22b.pcf ter-x24n.pcf ter-x24b.pcf ter-x28n.pcf ter-x28b.pcf ter-x32n.pcf ter-x32b.pcf
PCF_8BIT = $(PCF_8859_1) $(PCF_8859_2) $(PCF_8859_5) $(PCF_8859_7) $(PCF_8859_9) $(PCF_MS_1251) $(PCF_8859_13) $(PCF_8859_15) $(PCF_8859_16) $(PCF_IBM_437) $(PCF_KOI8_R) $(PCF_PT_154) $(PCF_KOI8_U)
PCF = $(PCF_10646_1)
OTB = ter-u12n.otb ter-u12b.otb ter-u14n.otb ter-u14b.otb ter-u16n.otb ter-u16b.otb ter-u18n.otb ter-u18b.otb ter-u20n.otb ter-u20b.otb ter-u22n.otb ter-u22b.otb ter-u24n.otb ter-u24b.otb ter-u28n.otb ter-u28b.otb ter-u32n.otb ter-u32b.otb
# Default
all: $(PSF) $(PCF)
DESTDIR =
prefix = /usr/local
psfdir = $(prefix)/share/consolefonts
x11dir = $(prefix)/share/fonts/terminus
otbdir = $(prefix)/share/fonts/terminus
install: $(PSF) $(PCF)
mkdir -p $(DESTDIR)$(psfdir)
for i in $(PSF) ; do gzip -c $$i > $(DESTDIR)$(psfdir)/$$i.gz ; done
mkdir -p $(DESTDIR)$(x11dir)
for i in $(PCF) ; do gzip -c $$i > $(DESTDIR)$(x11dir)/$$i.gz ; done
uninstall:
for i in $(PSF) ; do rm -f $(DESTDIR)$(psfdir)/$$i.gz ; done
for i in $(PCF) ; do rm -f $(DESTDIR)$(x11dir)/$$i.gz ; done
fontdir:
mkfontscale $(DESTDIR)$(x11dir)
mkfontdir $(DESTDIR)$(x11dir)
fc-cache -f $(DESTDIR)$(x11dir)
# Linux Console
VGA_8859_1 = uni/vgagr.uni uni/ascii-h.uni uni/win-1252.uni
VGA_8859_2 = uni/vgagr.uni uni/ascii-h.uni uni/vga-1250.uni uni/8859-2.uni
VGA_8859_7 = uni/vgagr.uni uni/ascii-h.uni uni/vga-1253.uni uni/8859-7.uni
VGA_8859_9 = uni/vgagr.uni uni/ascii-h.uni uni/win-1254.uni
VGA_MS_1251 = uni/vgagr.uni uni/ascii-h.uni uni/vga-1251.uni uni/win-1251.uni
VGA_8859_13 = uni/vgagr.uni uni/ascii-h.uni uni/vga-1257.uni uni/8859-13.uni
VGA_8859_16 = uni/vgagr.uni uni/ascii-h.uni uni/nls-1250.uni uni/8859-16.uni
VGA_MS_1255 = uni/vgagr.uni uni/ascii-h.uni uni/win-1255.uni
VGA_IBM_437 = uni/cntrl.uni uni/ascii-h.uni uni/ibm-437.uni
VGA_KOI8_RV = uni/cntrl.uni uni/ascii-h.uni uni/koibm8-r.uni
VGA_KOI8_R = uni/cntrl.uni uni/ascii-h.uni uni/koi8-r.uni
VGA_BG_MIK = uni/cntrl.uni uni/ascii-h.uni uni/bg-mik.uni
VGA_PT_154 = uni/vgagr.uni uni/ascii-h.uni uni/pt-154.uni
VGA_KOI8_UV = uni/cntrl.uni uni/ascii-h.uni uni/koibm8-u.uni
VGA_KOI8_U = uni/cntrl.uni uni/ascii-h.uni uni/koi8-u.uni
VGA_XOS4_2 = uni/xos4-2.uni
DUP_8859_1 = dup/vgagr.dup dup/ascii-h.dup
DUP_8859_2 = dup/vgagr.dup dup/ascii-h.dup
DUP_8859_7 = dup/vgagr.dup dup/ascii-h.dup
DUP_8859_9 = dup/vgagr.dup dup/ascii-h.dup
DUP_MS_1251 = dup/vgagr.dup dup/ascii-h.dup
DUP_8859_13 = dup/vgagr.dup dup/ascii-h.dup
DUP_8859_16 = dup/vgagr.dup dup/ascii-h.dup
DUP_MS_1255 = dup/vgagr.dup dup/ascii-h.dup
DUP_IBM_437 = dup/cntrl.dup dup/ascii-h.dup dup/ibm-437.dup
DUP_KOI8_RV = dup/cntrl.dup dup/ascii-h.dup dup/koi8.dup
DUP_KOI8_R = dup/cntrl.dup dup/ascii-h.dup dup/koi8.dup
DUP_BG_MIK = dup/cntrl.dup dup/ascii-h.dup dup/ibm-437.dup
DUP_PT_154 = dup/vgagr.dup dup/ascii-h.dup
DUP_KOI8_UV = dup/cntrl.dup dup/ascii-h.dup dup/koi8.dup
DUP_KOI8_U = dup/cntrl.dup dup/ascii-h.dup dup/koi8.dup
DUP_XOS4_2 = dup/vgagr.dup dup/xos4-2.dup
$(PSF_8859_1) $(PSF_VGAW_8859_1): ter-1%.psf : ter-u%.bdf $(VGA_8859_1) $(DUP_8859_1)
$(UCS2ANY) $< $(REG_8859_1) $(VGA_8859_1) | $(BDF2PSF) -o $@ $(DUP_8859_1)
$(PSF_8859_2) $(PSF_VGAW_8859_2): ter-2%.psf : ter-u%.bdf $(VGA_8859_2) $(DUP_8859_2)
$(UCS2ANY) $< $(REG_8859_2) $(VGA_8859_2) | $(BDF2PSF) -o $@ $(DUP_8859_2)
$(PSF_8859_7) $(PSF_VGAW_8859_7): ter-7%.psf : ter-u%.bdf $(VGA_8859_7) $(DUP_8859_7)
$(UCS2ANY) $< $(REG_8859_7) $(VGA_8859_7) | $(BDF2PSF) -o $@ $(DUP_8859_7)
$(PSF_8859_9) $(PSF_VGAW_8859_9): ter-9%.psf : ter-u%.bdf $(VGA_8859_9) $(DUP_8859_9)
$(UCS2ANY) $< $(REG_8859_9) $(VGA_8859_9) | $(BDF2PSF) -o $@ $(DUP_8859_9)
$(PSF_MS_1251) $(PSF_VGAW_MS_1251): ter-c%.psf : ter-u%.bdf $(VGA_MS_1251) $(DUP_MS_1251)
$(UCS2ANY) $< $(REG_MS_1251) $(VGA_MS_1251) | $(BDF2PSF) -o $@ $(DUP_MS_1251)
$(PSF_8859_13) $(PSF_VGAW_8859_13): ter-d%.psf : ter-u%.bdf $(VGA_8859_13) $(DUP_8859_13)
$(UCS2ANY) $< $(REG_8859_13) $(VGA_8859_13) | $(BDF2PSF) -o $@ $(DUP_8859_13)
$(PSF_8859_16) $(PSF_VGAW_8859_16): ter-g%.psf : ter-u%.bdf $(VGA_8859_16) $(DUP_8859_16)
$(UCS2ANY) $< $(REG_8859_16) $(VGA_8859_16) | $(BDF2PSF) -o $@ $(DUP_8859_16)
$(PSF_MS_1255) $(PSF_VGAW_MS_1255): ter-h%.psf : ter-u%.bdf $(VGA_MS_1255) $(DUP_MS_1255)
$(UCS2ANY) $< $(REG_MS_1255) $(VGA_MS_1255) | $(BDF2PSF) -o $@ $(DUP_MS_1255)
$(PSF_IBM_437) $(PSF_VGAW_IBM_437): ter-i%.psf : ter-u%.bdf $(VGA_IBM_437) $(DUP_IBM_437)
$(UCS2ANY) $< $(REG_IBM_437) $(VGA_IBM_437) | $(BDF2PSF) -o $@ $(DUP_IBM_437)
$(PSF_KOI8_RV) $(PSF_VGAW_KOI8_RV): ter-k%.psf : ter-u%.bdf $(VGA_KOI8_RV) $(DUP_KOI8_RV)
$(UCS2ANY) $< $(REG_KOI8_R) $(VGA_KOI8_RV) | $(BDF2PSF) -o $@ $(DUP_KOI8_RV)
$(PSF_KOI8_R): ter-k%.psf : ter-u%.bdf $(VGA_KOI8_R) $(DUP_KOI8_R)
$(UCS2ANY) $< $(REG_KOI8_R) $(VGA_KOI8_R) | $(BDF2PSF) -o $@ $(DUP_KOI8_R)
$(PSF_BG_MIK) $(PSF_VGAW_BG_MIK): ter-m%.psf : ter-u%.bdf $(VGA_BG_MIK) $(DUP_BG_MIK)
$(UCS2ANY) $< $(REG_BG_MIK) $(VGA_BG_MIK) | $(BDF2PSF) -o $@ $(DUP_BG_MIK)
$(PSF_PT_154) $(PSF_VGAW_PT_154): ter-p%.psf : ter-u%.bdf $(VGA_PT_154) $(DUP_PT_154)
$(UCS2ANY) $< $(REG_PT_154) $(VGA_PT_154) | $(BDF2PSF) -o $@ $(DUP_PT_154)
$(PSF_KOI8_UV) $(PSF_VGAW_KOI8_UV): ter-u%.psf : ter-u%.bdf $(VGA_KOI8_UV) $(DUP_KOI8_UV)
$(UCS2ANY) $< $(REG_KOI8_R) $(VGA_KOI8_UV) | $(BDF2PSF) -o $@ $(DUP_KOI8_UV)
$(PSF_KOI8_U): ter-u%.psf : ter-u%.bdf $(VGA_KOI8_U) $(DUP_KOI8_U)
$(UCS2ANY) $< $(REG_KOI8_U) $(VGA_KOI8_U) | $(BDF2PSF) -o $@ $(DUP_KOI8_U)
$(PSF_XOS4_2) $(PSF_VGAW_XOS4_2): ter-v%.psf : ter-u%.bdf $(VGA_XOS4_2) $(DUP_XOS4_2)
$(UCS2ANY) $< $(REG_XOS4_2) $(VGA_XOS4_2) | $(BDF2PSF) -o $@ $(DUP_XOS4_2)
psf: $(PSF)
install-psf: $(PSF)
mkdir -p $(DESTDIR)$(psfdir)
for i in $(PSF) ; do gzip -c $$i > $(DESTDIR)$(psfdir)/$$i.gz ; done
uninstall-psf:
for i in $(PSF) ; do rm -f $(DESTDIR)$(psfdir)/$$i.gz ; done
psf-vgaw: $(PSF_VGAW)
install-psf-vgaw: $(PSF_VGAW)
mkdir -p $(DESTDIR)$(psfdir)
for i in $(PSF_VGAW) ; do gzip -c $$i > $(DESTDIR)$(psfdir)/$$i.gz ; done
uninstall-psf-vgaw:
for i in $(PSF_VGAW) ; do rm -f $(DESTDIR)$(psfdir)/$$i.gz ; done
psfref = $(psfdir)/README.terminus
install-psf-ref: README
mkdir -p $(DESTDIR)$(psfdir)
sed -e"/^2\.4/,/^2\.5/p" -n README | grep -v "^2\." > $(DESTDIR)$(psfref)
uninstall-psf-ref:
rm -f $(DESTDIR)$(psfref)
# X11 Window System
X11_8859_1 = uni/x11gr.uni uni/ascii-h.uni uni/win-1252.uni
X11_8859_2 = uni/x11gr.uni uni/ascii-h.uni uni/empty.uni uni/8859-2.uni
X11_8859_5 = uni/x11gr.uni uni/ascii-h.uni uni/empty.uni uni/8859-5.uni
X11_8859_7 = uni/x11gr.uni uni/ascii-h.uni uni/empty.uni uni/8859-7.uni
X11_8859_9 = uni/x11gr.uni uni/ascii-h.uni uni/win-1254.uni
X11_MS_1251 = uni/x11gr.uni uni/ascii-h.uni uni/x11-1251.uni uni/win-1251.uni
X11_8859_13 = uni/x11gr.uni uni/ascii-h.uni uni/x11-1257.uni uni/8859-13.uni
X11_8859_15 = uni/x11gr.uni uni/ascii-h.uni uni/empty.uni uni/8859-15.uni
X11_8859_16 = uni/x11gr.uni uni/ascii-h.uni uni/empty.uni uni/8859-16.uni
X11_IBM_437 = uni/cntrl.uni uni/ascii-h.uni uni/ibm-437.uni
X11_KOI8_R = uni/x11gr.uni uni/ascii-h.uni uni/koi8-r.uni
X11_PT_154 = uni/x11gr.uni uni/ascii-h.uni uni/pt-154.uni
X11_KOI8_U = uni/x11gr.uni uni/ascii-h.uni uni/koi8-u.uni
X11_10646_1 = uni/x11gr.uni uni/10646-1.uni
$(PCF_8859_1): ter-1%.pcf : ter-u%.bdf $(X11_8859_1)
$(UCS2X11) $< $(REG_8859_1) $(X11_8859_1) | $(BDF2PCF) -o $@
$(PCF_8859_2): ter-2%.pcf : ter-u%.bdf $(X11_8859_2)
$(UCS2X11) $< $(REG_8859_2) $(X11_8859_2) | $(BDF2PCF) -o $@
$(PCF_8859_5): ter-5%.pcf : ter-u%.bdf $(X11_8859_5)
$(UCS2X11) $< $(REG_8859_5) $(X11_8859_5) | $(BDF2PCF) -o $@
$(PCF_8859_7): ter-7%.pcf : ter-u%.bdf $(X11_8859_7)
$(UCS2X11) $< $(REG_8859_7) $(X11_8859_7) | $(BDF2PCF) -o $@
$(PCF_8859_9): ter-9%.pcf : ter-u%.bdf $(X11_8859_9)
$(UCS2X11) $< $(REG_8859_9) $(X11_8859_9) | $(BDF2PCF) -o $@
$(PCF_MS_1251): ter-c%.pcf : ter-u%.bdf $(X11_MS_1251)
$(UCS2X11) $< $(REG_MS_1251) $(X11_MS_1251) | $(BDF2PCF) -o $@
$(PCF_8859_13): ter-d%.pcf : ter-u%.bdf $(X11_8859_13)
$(UCS2X11) $< $(REG_8859_13) $(X11_8859_13) | $(BDF2PCF) -o $@
$(PCF_8859_15): ter-f%.pcf : ter-u%.bdf $(X11_8859_15)
$(UCS2X11) $< $(REG_8859_15) $(X11_8859_15) | $(BDF2PCF) -o $@
$(PCF_8859_16): ter-g%.pcf : ter-u%.bdf $(X11_8859_16)
$(UCS2X11) $< $(REG_8859_16) $(X11_8859_16) | $(BDF2PCF) -o $@
$(PCF_IBM_437): ter-i%.pcf : ter-u%.bdf $(X11_IBM_437)
$(UCS2X11) $< $(REG_IBM_437) $(X11_IBM_437) | $(BDF2PCF) -o $@
$(PCF_KOI8_R): ter-k%.pcf : ter-u%.bdf $(X11_KOI8_R)
$(UCS2X11) $< $(REG_KOI8_R) $(X11_KOI8_R) | $(BDF2PCF) -o $@
$(PCF_PT_154): ter-p%.pcf : ter-u%.bdf $(X11_PT_154)
$(UCS2X11) $< $(REG_PT_154) $(X11_PT_154) | $(BDF2PCF) -o $@
$(PCF_KOI8_U): ter-u%.pcf : ter-u%.bdf $(X11_KOI8_U)
$(UCS2X11) $< $(REG_KOI8_U) $(X11_KOI8_U) | $(BDF2PCF) -o $@
$(PCF_10646_1): ter-x%.pcf : ter-u%.bdf $(X11_10646_1)
$(UCS2X11) $< $(REG_10646_1) $(X11_10646_1) | $(BDF2PCF) -o $@
pcf: $(PCF)
install-pcf: $(PCF)
mkdir -p $(DESTDIR)$(x11dir)
for i in $(PCF) ; do gzip -c $$i > $(DESTDIR)$(x11dir)/$$i.gz ; done
uninstall-pcf:
for i in $(PCF) ; do rm -f $(DESTDIR)$(x11dir)/$$i.gz ; done
pcf-8bit: $(PCF_8BIT)
install-pcf-8bit: $(PCF_8BIT)
mkdir -p $(DESTDIR)$(x11dir)
for i in $(PCF_8BIT) ; do gzip -c $$i > $(DESTDIR)$(x11dir)/$$i.gz ; done
uninstall-pcf-8bit:
for i in $(PCF_8BIT) ; do rm -f $(DESTDIR)$(x11dir)/$$i.gz ; done
# Open Type Bitmap
$(OTB): ter-u%.otb : ter-u%.bdf
$(BDF2OTB) -o $@ $<
otb: $(OTB)
install-otb: $(OTB)
mkdir -p $(DESTDIR)$(otbdir)
cp -f $(OTB) $(DESTDIR)$(otbdir)
uninstall-otb:
for i in $(OTB) ; do rm -f $(DESTDIR)$(otbdir)/$$i ; done
# Cleanup
clean:
rm -f $(PSF) $(PSF_VGAW) $(PCF) $(PCF_8BIT) $(OTB)
.PHONY: all install uninstall fontdir psf install-psf uninstall-psf psf-vgaw install-psf-vgaw uninstall-psf-vgaw install-psf-ref uninstall-psf-ref pcf install-pcf uninstall-pcf pcf-8bit install-pcf-8bit uninstall-pcf-8bit otb install-otb uninstall-otb clean
+1
View File
@@ -0,0 +1 @@
See CHANGES
+94
View File
@@ -0,0 +1,94 @@
Copyright (C) 2020 Dimitar Toshkov Zhekov,
with Reserved Font Name "Terminus Font".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+395
View File
@@ -0,0 +1,395 @@
Contents:
1. About.
1.1. Build requitements.
1.2. Quick installation.
1.3. Legend.
1.4. Variants.
1.5. Notes.
1.6. Alternative tools.
2. Linux console.
2.1. Installation.
2.2. Usage.
2.3. Quick reference.
2.4. Legend.
2.5. Notes.
3. X11 Window System.
3.1. Installation.
3.2. Notes.
4. Open Type Bitmap.
4.1. Installation.
4.2. Notes.
5. Microsoft Windows.
5.1. Installation package.
5.2. Font file only.
5.3. Notes.
6. Frequently Asked Questions.
7. Legal information.
7.1. Licenses.
7.2. Copyright.
--
1. About.
This archive contains source code for generating and installing Terminus
Font for Linux console, X11 Window System, Microsoft Windows and Open Type
Bitmap capable systems.
- version 4.49.1
- sizes 6x12, 8x14, 8x16, 10x18, 10x20, 11x22, 12x24, 14x28, 16x32
- weights normal, bold, CRT VGA bold
- characters 1356
- format Bitmap Distribution Format (BDF) version 2.1
The character set covers about 120 language sets and supports ISO8859-1/2/5/
7/9/13/15/16, Paratype-PT154/PT254, KOI8-R/U/E/F, Esperanto and many IBM,
Windows and Macintosh code pages, as well as the IBM VGA, vt100 and xterm
pseudographic characters.
1.1. Build requirements.
- GNU make
- Python 3.5.0+ (or node.js 6.9.0+ as alternative)
- for X11 only: bdftopcf
- for Windows only: GCC for Win32/i686
- for the Windows installer only: NSIS and patch(1).
1.2. Quick installation.
The commands:
$ ./configure [--prefix=PREFIX]
$ make -j8
# make install fontdir
compile and install the Linux console and X11 Window System fonts.
The default PREFIX is /usr/local.
1.3. Legend.
The file names are structured as follows:
ter-u<SIZE><STYLE>.bdf
<SIZE> is the font height. <STYLE> is n for normal (all sizes), b for bold
(all sizes except 6x12), and v for CRT VGA bold (8x14 and 8x16 only, uses
the eight character matrix column).
1.4. Variants.
Some characters are implemented in two variants. To use the alternate
variant, execute:
$ patch -p1 -i alt/<NAME>.diff
before making the font. See the font WEB page for examples. If you want to
combine hi2 with dv1 and/or ka2, apply hi2 and then hi2-dv1 and/or hi2-ka2.
The default variant for unicode range 2800-28FF is oriented towards
pseudographics.
1.5. Notes.
The commands marked with $ can be executed by a regular user.
The configure commands are optional.
"make -j8" runs 8 jobs in parallel.
Sizes 6x12, 11x22, 14x28-bold and 16x32-normal are worse than the others.
Avoid them.
210E and 210F are not italic.
226A, 226B and the double struck letters are not very good.
2135 may be wrong.
The hebrew letters and sheqel are uppercase height, which is an attempt to
compensate for the missing width.
1.6. Alternative tools.
If your Python 3 executable is named python instead if python3:
$ ./configure INT=python
To use node.js instead of python:
$ ./configure INT=node EXT=js
--
2. Linux console.
- weights normal, bold, CRT VGA-bold
- code pages ISO8859-1/ISO8859-15/Windows-1252, ISO8859-2/Windows-1250,
Windows-1251/ISO8859-5, ISO8859-9/Windows-1254, ISO8859-16,
ISO8859-7/Windows-1253, ISO8859-13/Windows-1257, IBM-437,
Bulgarian-MIK, KOI8-R, KOI8-U, Paratype-PT154, combined
- format PC Screen Font (PSF) with unicode data
2.1. Installation.
$ ./configure [--prefix=PREFIX | --psfdir=DIRECTORY]
$ make -j8 psf
# make install-psf
The files are compressed with gzip and installed in DIRECTORY. The default
DIRECTORY is PREFIX/share/consolefonts. For kbd, you may need to change it
to PREFIX/lib/kbd/consolefonts or PREFIX/share/kbd/consolefonts, depending
on the kbd version.
The CRT VGA bold weight fonts, suitable for real CRT text modes only, are
not installed by default. To install them, execute:
$ make -j8 psf-vgaw
# make install-psf-vgaw
2.2. Usage.
To load a cont in consoletools:
$ consolechars [-m MAPPING] -f ter-<X><SIZE><STYLE>
To load a font in kbd:
$ setfont [-m MAPPING] ter-<X><SIZE><STYLE>
where <X> is a character identifying the code page as listed in p.2.4.
2.3. Quick reference.
The commands:
$ ./configure [--prefix=PREFIX | --psfdir=DIRECTORY | --psfref=FILENAME]
# make install-psf-ref
install the text from p.2.4 as FILENAME (the default is README.terminus)
in DIRECTORY.
2.4. Legend.
names mappings covered codepage(s)
ter-1* iso01, iso15, cp1252 ISO8859-1, ISO8859-15, Windows-1252
ter-2* iso02, cp1250 ISO8859-2, Windows-1250
ter-7* iso07, cp1253 ISO8859-7, Windows-1253
ter-9* iso09, cp1254 ISO8859-9, Windows-1254
ter-c* cp1251, iso05 Windows-1251, ISO8859-5
ter-d* iso13, cp1257 ISO8859-13, Windows-1257
ter-g* iso16 ISO8859-16
ter-h* cp1255, iso08 Windows-1258, ISO8859-8
ter-i* cp437 IBM-437
ter-k* koi8r KOI8-R
ter-m* mik Bulgarian-MIK
ter-p* pt154 Paratype-PT154
ter-u* koi8u KOI8-U
ter-v* all mappings / code pages listed above and many others, about 110
language sets, 8 or 16 foreground colors depending on the kernel and
console driver versions
names weight
ter-*n normal
ter-*b bold
ter-*v CRT VGA bold
2.5. Notes.
The combined code page is based on IBM-437 (character 0xFF is ogonek).
The ISO8859-16 font also includes all letters and accents from Windows-1250.
--
3. X11 Window System.
- weights normal, bold
- code pages ISO8859-1/Windows-1252, ISO8859-2, ISO8859-5, ISO8859-7,
ISO8859-9/Windows-1254, ISO8859-13, ISO8859-15, ISO8859-16,
Windows-1251, IBM-437, KOI8-R, KOI8-U, Paratype-PT154 and
ISO10646-1 (unicode)
- format Portable Compiled Font (PCF)
3.1. Installation.
$ ./configure [--prefix=PREFIX | --x11dir=DIRECTORY]
$ make -j8 pcf
# make install-pcf
The files are compressed with gzip and installed in DIRECTORY. The default
DIRECTORY is PREFIX/share/fonts/terminus. Requires bdftopcf.
A copy of the normal 6x12 font is installed as "bold", because some X11
libraries and applications substitute the missing bold fonts by shifting the
normal fonts, and others do not recognize the bold weight at all if the
lowest font size lacks it.
To update the font cache in DIRECTORY after (un)installation, run:
# make fontdir
The configuration file which lists the font directories must contain
DIRECTORY. The X11 server may need to be restarted so the font list can be
updated.
By default, only the unicode (ISO10646-1) font is installed. To install the
other code pages:
$ make -j8 pcf-8bit
# make install-pcf-8bit
3.2. Notes.
The ISO8859-1 and ISO8859-9 fonts contain the Windows Western characters and
can be used as Windows-1252 and Windows-1254 respectively.
--
4. Open Type Bitmap.
- weights normal, bold
- code pages ISO10646-1 (unicode)
- format Linux/UNIX bitmap-only TrueType
4.1. Installation.
$ ./configure [--prefix=PREFIX | --otbdir=DIRECTORY]
$ make otb
# make install-otb
The files are installed in DIRECTORY.
The default DIRECTORY is PREFIX/share/fonts/terminus.
4.2. Notes
An OTB file is generated for each BDF file, instead of combining sizes by
weight, for the following reasons:
- KDE compatibility. With combined files, only the first size is available.
- Correct global font metrics. For example, the base line and character
width for 8x14 and 8x16 (relative to height) may never be identical.
otb1cli is NOT a full-featured BDF to sfnt convertor. It expands the bitmaps
first, uses the expanded widths, does not support overlapping characters,
and targets Linux/UNIX only.
--
5. Microsoft Windows.
- weights normal, bold
- code pages Windows-1252, 1250, 1253, 1254, 1251 and 1257
- format Font File Format version 2.0, compiled into FON
5.1. Installation package.
Make sure that no variant patches are applied to the font (p.1.4).
> cd win32
> build
Be patient, the process may take several minutes.
After that, open terminus.nsi and compile it.
5.2. Font file only.
> copy *.bdf win32
> cd win32
> make -j8
You can install terminus.fon via the regular means.
5.3. Notes.
The Windows code pages contain a total of 384 characters. All other
characters (math, pseudographics etc.) are not currently available.
--
6. Frequently Asked Questions.
Q. Italic version?
A. No. The quality is significantly lower, and preserving the font width
requires overlapping characters, which are not handled very well by X11/Xft.
You can try mkitalic from FreeBSD or bdfslant from Debian.
Q. Scalable version?
A. Probably not. The font uses a lot of straight horizontal and vertical
lines, which is good for a bitmap, but not so much for a vector font. A
bitmap font packaged as TTF seems possible.
Q. How about some new characters?
A. Contact me and be ready to help.
Q. The bold 6x12 font...
A. ...does not exist, there is no space for a bold font in a 6x12 matrix.
However, the "normal" font is somewhere between.
Q. The font works in X11/Motif, but not in GNOME/KDE/Xfce.
A. Try adding 75-yes-terminus.conf to the Fontconfig configuration files.
For some Fontconfig versions, you may need to replace the text "Terminus"
in 75-yes-terminus.conf with "xos4 Terminus", though that is unlikely.
See also mkfontscale(1), mkfontdir(1), fc-cache(1), xorg.conf(5), xfs(1),
xlsfonts(1), fonts-conf(5) etc.
Q. My terminal emulator does not display cyrillic/pseudographics/...
A. If you have the 8-bit X11 code pages installed, and your emulator uses
"XLFD" font names, make sure that the font name ends with "-10616-1" instead
of "-*-*".
--
7. Legal information.
7.1. Licenses.
Terminus Font is licensed under the SIL Open Font License, Version 1.1.
The license is included as OFL.TXT, and is also available with a FAQ at:
http://scripts.sil.org/OFL
The font includes two variants of unicode range 2800-28FF, but does not
support Braille in any way.
The configure files, python and javascript sources are distributed under
the GNU General Public License version 2.0 or (at your choice) any later
version.
7.2. Copyright.
Terminus Font 4.49.1, Copyright (C) 2020 Dimitar Toshkov Zhekov.
Report bugs to <dimitar.zhekov@gmail.com>
Thanks to Anton Zinoviev, Tim Allen, Kir Koliushkin, Antonios Galanopoulos
and everyone else who helped.
+395
View File
@@ -0,0 +1,395 @@
Съдържание:
1. Обща информация.
1.1. Изисквания за компилация.
1.2. Бърза инсталация.
1.3. Легенда.
1.4. Варианти.
1.5. Забележки.
1.6. Алтернативни средства.
2. Линукс конзола.
2.1. Инсталация.
2.2. Използване.
2.3. Кратко описание.
2.4. Легенда.
2.5. Забележки.
3. X11 Window System.
3.1. Инсталация.
3.2. Забележки.
4. Open Type Bitmap.
4.1. Инсталация.
4.2. Забележки.
5. Microsoft Windows.
5.1. Инсталационен пакет.
5.2. Само файл с шрифта.
5.3. Забележки.
6. Често задавани въпроси.
7. Правна информация.
7.1. Лицензи.
7.2. Авторство.
--
1. Обща информация.
Този архив съдържа изходен код за генериране и инсталиране на шрифта
Терминус за Линукс конзола, X11 Window System, Microsoft Windows и системи
поддържащи Open Type Bitmap.
- версия 4.49.1
- размери 6x12, 8x14, 8x16, 10x18, 10x20, 11x22, 12x24, 14x28, 16x32
- тежести нормален, удебелен, CRT VGA-удебелен
- символи 1356
- формат Bitmap Distribution Format (BDF) версия 2.1
Набора символи покрива около 120 езикови набора и поддържа ISO8859-1/2/5/7/
9/13/15/16, Paratype-PT154/PT254, KOI8-R/U/E/F, Есперанто, много кодови
страници на IBM, Windows и Macintosh, а също и превдографичните символи на
VGA, vt100 и xterm.
1.1. Изисквания за компилация.
- GNU make
- Python 3.5.0+ (или node.js 6.9.0+ като алтернатива)
- само за X11: bdftopcf
- само за Windows: GCC за Win32/i686
- само за Windows инсталатора: NSIS и patch(1).
1.2. Бърза инсталация.
Командите:
$ ./configure [--prefix=PREFIX]
$ make -j8
# make install fontdir
компилират и инсталират шрифтовете за Линукс конзола и X11 Window System.
Подразбиращият се префикс е /usr/local.
1.3. Легенда.
Имената на файловете са структурирани както следва:
ter-u<РАЗМЕР><СТИЛ>.bdf
където <РАЗМЕР> е височината в точки, а <СТИЛ> е n за нормален (всички
размери), b за удебелен (всички размери освен 6x12), и v за CRT VGA удебелен
(само за 8x14 и 8x16, използва 8 колона на символната матрица).
1.4. Варианти.
Някои символи са реализирани в два варианта. За да използвате алтернативния
вариант, преди инсталация изпълнете:
$ patch -p1 -i alt/<ИМЕ>.diff
Примери за разликите межда вариантите са дадени на страницата на шрифта.
Ако желаете да комбинирате hi2 с dv1 и/или ka2, приложете първо hi2, и след
това hi2-dv1 и/или hi2-ka2. Подразбиращия се вариант на юникод обхвата
2800-28FF е ориентиран към псевдографика.
1.5. Забележки.
Командите отбелязани с $ могат да се изпълняват от обикновен потребител.
Командите за конфигуриране не са задължителни.
"make -j8" изпълнява паралелно 8 задачи.
Размери 6x12, 11x22, 14x28-удебелен и 16x32-нормален са с по-лошо качество
от останалите. Избягвайте ги.
210E и 210F не са наклонени.
226A, 226B и двойно начертаните букви не са много добри.
2135 може би е грешен.
В опит да се компенсира липсващата ширина, буквите от иврит и знака шекел са
с височина на главни букви.
1.6. Алтернативни средства.
Ако изпълнимия файл на Python 3 интерпретатора е python вместо python3:
$ ./configure INT=python
За използване на node.js вместо python:
$ ./configure INT=node EXT=js
--
2. Линукс конзола.
- тежести нормален, удебелен, CRT VGA-удебелен
- кодировки ISO8859-1/ISO8859-15/Windows-1252, ISO8859-2/Windows-1250,
Windows-1251/ISO8859-5, ISO8859-9/Windows-1254, ISO8859-16,
ISO8859-7/Windows-1253, ISO8859-13/Windows-1257, IBM-437,
Bulgarian-MIK, KOI8-R, KOI8-U, Paratype-PT154, комбинирана
- формат PC Screen Font (PSF) с unicode данни
2.1. Инсталация.
$ ./configure [--prefix=ПРЕФИКС | --psfdir=ДИРЕКТОРИЯ]
$ make -j8 psf
# make install-psf
Файловете се компресират с gzip и инсталират в ДИРЕКТОРИЯ. Подразбиращата се
ДИРЕКТОРИЯ е ПРЕФИКС/share/consolefonts. За kbd може да се наложи да смените
директорията на PREFIX/lib/kbd/consolefonts или
PREFIX/share/kbd/consolefonts, в зависимост от версията на kbd.
CRT VGA-удебелените шрифтове, подходящи за само истински CRT текстови
режими, не се инсталират по подразбиране. За инсталирането им изпълнете:
$ make -j8 psf-vgaw
# make install-psf-vgaw
2.2. Използване.
За зареждане на шрифт с consoletools:
$ consolechars [-m КОДИРОВКА] -f ter-<X><РАЗМЕР><СТИЛ>
За зареждане на шрифт с kbd:
$ setfont [-m MAPPING] ter-<X><SIZE><STYLE>
където <X> е символ идентифициращ кодовата страница по списъка от т.2.4.
2.3. Кратко описание.
Командите:
$ ./configure [--prefix=ПРЕФИКС | --psfdir=ДИРЕКТОРИЯ | --psfref=ИМЕНАФАЙЛ]
# make install-psf-ref
инсталират текста от т.2.4 (на английски) под името ИМЕНАФАЙЛ (подразбира
се README.terminus) в ДИРЕКТОРИЯ.
2.4. Легенда.
имена кодировки кодови страници
ter-1* iso01, iso15, cp1252 ISO8859-1, ISO8859-15, Windows-1252
ter-2* iso02, cp1250 ISO8859-2, Windows-1250
ter-7* iso07, cp1253 ISO8859-7, Windows-1253
ter-9* iso09, cp1254 ISO8859-9, Windows-1254
ter-c* cp1251, iso05 Windows-1251, ISO8859-5
ter-d* iso13, cp1257 ISO8859-13, Windows-1257
ter-g* iso16 ISO8859-16
ter-h* cp1255, iso08 Windows-1255, ISO8859-8
ter-i* cp437 IBM-437
ter-k* koi8r KOI8-R
ter-m* mik Bulgarian-MIK
ter-p* pt154 Paratype-PT154
ter-k* koi8u KOI8-U
ter-v* всички изброени по-горе кодировки / страници и много други, около
110 езикови набора, 8 или 16 цвята за текст в зависимост от ядрото и
конзолния драйвер
имена тежест
ter-*n нормален
ter-*b удебелен
ter-*v CRT VGA-удебелен
2.5. Забележки.
Комбинираната кодова страница е базирана IBM-437 (символ 0xFF е ogonek).
ISO8859-16 шрифта включва също всички букви и акценти от Windows-1250.
--
3. X11 Window System.
- тежести нормален, удебелен
- кодировки ISO8859-1/Windows-1252, ISO8859-2, ISO8859-5, ISO8859-7,
ISO8859-9/Windows-1254, ISO8859-13, ISO8859-15, ISO8859-16,
Windows-1251, IBM-437, KOI8-R, KOI8-U, Paratype-PT154 и
ISO10646-1 (уникод)
- формат Portable Compiled Font (PCF)
3.1. Инсталация.
$ ./configure [--prefix=ПРЕФИКС | --x11dir=ДИРЕКТОРИЯ]
$ make pcf
# make install-pcf
Файловете се компресират с gzip и инсталират в ДИРЕКТОРИЯ. Подразбиращата се
ДИРЕКТОРИЯ е ПРЕФИКС/share/fonts/terminus. Изисква се bdftopcf.
Копие на нормалния 6x12 шрифт се инсталира като "удебелен", защото някои
X11 библиотеки и програми заменят липсващите удебелени шрифтове чрез
отместване на нормалните, а други изобщо не разпознават удебелена тежест,
ако такава липсва в най-малкия размер на шрифта.
За да актуализирате fonts.dir в ДИРЕКТОРИЯ след (де)инсталация, изпълнете:
# make fontdir
Конфигурационния файл, изброяващ директориите за шрифтове, трябва да включва
ДИРЕКТОРИЯ. За да се обнови списъка от шрифтове може да се наложи рестарт на
X11 сървъра.
По подразбиране се инсталира само уникод (ISO10646-1) шрифта. За инсталиране
на останалите кодови страници:
$ make -j8 pcf-8bit
# make install-pcf-8bit
3.2. Забележки.
ISO8859-1 и ISO8859-9 шрифтовете съдържат Windows Western символите и могат
да бъдат използвани съответно като Windows-1252 и Windows-1254.
--
4. Open Type Bitmap.
- тежести нормален, удебелен
- кодировки ISO10646-1 (уникод)
- формат растерен TrueType за Linux/UNIX
4.1. Инсталация.
$ ./configure [--prefix=ПРЕФИКС | --otbdir=ДИРЕКТОРИЯ]
$ make otb
# make install-otb
Файловете се инсталират в ДИРЕКТОРИЯ.
Подразбиращата се ДИРЕКТОРИЯ е ПРЕФИКС/share/fonts/terminus.
4.2. Забележки.
За всеки BDF файл се генерира отделен OTB файл, вместо да се комбинират
по тежест, по следните причини:
- Съвместимост с КДЕ. При комбинирани файлове е достъпен само първия размер.
- Коректни глобални размери. Например базовата линия и ширината на символите
(измервани по височината) не могат да бъдат еднакви за 8x14 и 8x16.
otb1cli НЕ Е пълноценен конвертор от BDF към TTF. Символите предварително
се растеризират, използват се растеризизираните ширини, не се поддържат
застъпващи се символи, и резултата е предназначен само за Linux/UNIX.
--
5. Microsoft Windows.
- тежести нормален, удебелен
- кодировки Windows-1252, 1250, 1253, 1254, 1251 and 1257
- формат Font File Format версия 2.0, компилиран във FON
5.1. Инстанационен пакет.
Уверете се, че към шрифта не са прилагани корекции за варианти (т.1.4).
> cd win32
> build
Бъдете търпеливи, процеса може да отнеме няколко минути.
След това отворете terminus.nsi и го компилирайте.
5.2. Само файл с шрифта.
> copy *.bdf win32
> cd win32
> make -j8
Можете са инсталирате terminus.fon по стандартните начини.
5.3. Забележки.
Кодовите страници за Windows съдържат общо 384 символа. Всички останали
символи (математика, превдографика и т.н.) засега не са достъпни.
--
6. Често задавани въпроси.
В. Наклонена версия?
О. Не. Качеството е доста по-ниско, а и запазването ширината на символите
изисква припокриване, което не се обработва много добре от X11/Xft. Ако Ви
е много необходима, пробвайте mkitalic от FreeBSD или bdfslant от Debian.
В. Мащабируема версия?
О. Вероятно не. Шрифта използва мого хоризонтални и вертикални линии, които
са подходящш ра растерна графика, но не толкова за векторна. Може би
растерен шрифт, комплектован като TrueType.
В. Какво ще кажете за някои нови символи?
О. Пишете ми и имайте готовност да помогнете.
В. Удебеленият 6x12 шрифт...
О. ...не съществува, в матрица 6x12 няма достатъчно място за добър удебелен
шрифт. Всъщност "нормалният" шрифт е някъде по средата.
В. Шрифтът работи с X11/Motif, но не и с GNOME/KDE/Xfce.
О. Опитайте да добавите 75-yes-terminus.conf към конфигурационните файлове
на Fontconfig. За някои версии на Fontconfig, може да се наложи да замените
текста "Terminus" във файла с "xos4 Terminus", макар че е слабо вероятно.
Вижте също mkfontscale(1), mkfontdir(1), fc-cache(1), xorg.conf(5), xfs(1),
xlsfonts(1), fonts-conf(5) и т.н.
В. Моя терминален емулатор не извежда кирилица/псевдографика/...
О. Ако имате инсталирани 8-битовите кодови страници за X11, и емулатора
използва "XLFD" имена на шрифтовете, уверете се че името на шрифта завършва
на "-10616-1" вместо "-*-*".
--
7. Правна информация.
7.1. Лицензи.
Terminus Font е лицензизан под SIL Open Font License, версия 1.1.
Лицензът е включен като OFL.TXT, и е достъпен заедно с FAQ на адрес:
http://scripts.sil.org/OFL
Шрифтът включва два варианта на юникод обхвата 2800-28FF, но по никакъв
начин не поддържа Брайловата азбука.
Конфигурационните файлове, както и изходния код на python и javascript, се
разпространяват под GNU General Public License версия 2.0 или (по Ваше
усмотрение) която и да е по-късна версия.
7.2. Авторство.
Terminus Font 4.49.1, Copyright (C) 2020 Димитър Тошков Жеков.
Адрес за кореспонденция <dimitar.zhekov@gmail.com>
Благодаря на Антон Зиновиев, Тим Алън, Кир Колышкин, Антониос Галанопулос и
всички останали, които помогнаха.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+860
View File
@@ -0,0 +1,860 @@
--- ./ter-u12b.bdf.orig 2020-12-28 20:34:18.635118400 +0200
+++ ./ter-u12b.bdf 2020-12-28 20:34:21.775122800 +0200
@@ -10030,3 +10030,2 @@
-E0
-90
-90
+00
+00
@@ -10034,0 +10034 @@
+F0
@@ -10070,8 +10070,8 @@
-78
-88
-88
-88
-88
-78
-08
-70
+38
+48
+48
+48
+48
+FC
+84
+00
--- ./ter-u12n.bdf.orig 2020-12-28 20:34:18.605118300 +0200
+++ ./ter-u12n.bdf 2020-12-28 20:34:21.775122800 +0200
@@ -10030,3 +10030,2 @@
-E0
-90
-90
+00
+00
@@ -10034,0 +10034 @@
+F0
@@ -10070,8 +10070,8 @@
-78
-88
-88
-88
-88
-78
-08
-70
+38
+48
+48
+48
+48
+FC
+84
+00
--- ./ter-u14b.bdf.orig 2020-12-28 20:34:18.655118400 +0200
+++ ./ter-u14b.bdf 2020-12-28 20:34:21.815122800 +0200
@@ -11082,4 +11082,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -11088,0 +11088 @@
+FC
@@ -11127,9 +11127,9 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
--- ./ter-u14n.bdf.orig 2020-12-28 20:34:18.645118400 +0200
+++ ./ter-u14n.bdf 2020-12-28 20:34:21.785122800 +0200
@@ -11082,4 +11082,3 @@
-38
-44
-44
-48
+00
+00
+00
@@ -11088,0 +11088 @@
+7C
@@ -11127,8 +11126,0 @@
-3E
-42
-42
-42
-42
-42
-3E
-02
@@ -11135,0 +11128,8 @@
+44
+44
+44
+44
+44
+FE
+82
+00
--- ./ter-u14v.bdf.orig 2020-12-28 20:34:18.675118400 +0200
+++ ./ter-u14v.bdf 2020-12-28 20:34:21.815122800 +0200
@@ -11082,4 +11082,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -11088,0 +11088 @@
+FC
@@ -11127,9 +11127,9 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
--- ./ter-u16b.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16b.bdf 2020-12-28 20:34:21.825122800 +0200
@@ -12134,4 +12134,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -12140,0 +12140 @@
+FC
@@ -12183,10 +12183,10 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
+00
--- ./ter-u16n.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16n.bdf 2020-12-28 20:34:21.825122800 +0200
@@ -12134,4 +12134,3 @@
-38
-44
-44
-48
+00
+00
+00
@@ -12140,0 +12140 @@
+7C
@@ -12183,9 +12182,0 @@
-3E
-42
-42
-42
-42
-42
-3E
-02
-02
@@ -12192,0 +12184,9 @@
+44
+44
+44
+44
+44
+FE
+82
+00
+00
--- ./ter-u16v.bdf.orig 2020-12-28 20:34:18.695118400 +0200
+++ ./ter-u16v.bdf 2020-12-28 20:34:21.855122900 +0200
@@ -12134,4 +12134,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -12140,0 +12140 @@
+FC
@@ -12183,10 +12183,10 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
+00
--- ./ter-u18b.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18b.bdf 2020-12-28 20:34:21.865122900 +0200
@@ -13187,5 +13187,3 @@
-3E00
-6300
-6300
-6300
-6200
+0000
+0000
+0000
@@ -13195,0 +13194,2 @@
+7F00
+6180
@@ -13240,12 +13240,12 @@
-3F80
-6180
-6180
-6180
-6180
-6180
-6180
-6180
-3F80
-0180
-0180
-3F00
+1F00
+3300
+6300
+6300
+6300
+6300
+6300
+6300
+FF80
+C180
+C180
+0000
--- ./ter-u18n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18n.bdf 2020-12-28 20:34:21.865122900 +0200
@@ -13187,5 +13187,3 @@
-3C00
-4200
-4200
-4200
-4400
+0000
+0000
+0000
@@ -13195,0 +13194,2 @@
+7E00
+4100
@@ -13240,12 +13240,12 @@
-3F00
-4100
-4100
-4100
-4100
-4100
-4100
-4100
-3F00
-0100
-0100
-3E00
+0F00
+1100
+2100
+2100
+2100
+2100
+2100
+2100
+7F80
+4080
+4080
+0000
--- ./ter-u20b.bdf.orig 2020-12-28 20:34:18.735118500 +0200
+++ ./ter-u20b.bdf 2020-12-28 20:34:21.905122900 +0200
@@ -14239,5 +14239,4 @@
-3E00
-6300
-6300
-6300
-6200
+0000
+0000
+0000
+0000
@@ -14247,0 +14247 @@
+7F00
@@ -14297,12 +14297,12 @@
-3F80
-6180
-6180
-6180
-6180
-6180
-6180
-6180
-3F80
-0180
-0180
-3F00
+1F00
+3300
+6300
+6300
+6300
+6300
+6300
+6300
+FF80
+C180
+C180
+0000
--- ./ter-u20n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u20n.bdf 2020-12-28 20:34:21.865122900 +0200
@@ -14239,5 +14239,4 @@
-3C00
-4200
-4200
-4200
-4400
+0000
+0000
+0000
+0000
@@ -14247,0 +14247 @@
+7E00
@@ -14297,12 +14297,12 @@
-3F00
-4100
-4100
-4100
-4100
-4100
-4100
-4100
-3F00
-0100
-0100
-3E00
+0F00
+1100
+2100
+2100
+2100
+2100
+2100
+2100
+7F80
+4080
+4080
+0000
--- ./ter-u22b.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22b.bdf 2020-12-28 20:34:21.905122900 +0200
@@ -15291,8 +15291,5 @@
-3E00
-6300
-6180
-6180
-6180
-6300
-7F00
-6180
+0000
+0000
+0000
+0000
+7F80
@@ -15301,0 +15299 @@
+7F80
@@ -15303,2 +15301,4 @@
-6180
-7F00
+60C0
+60C0
+60C0
+7F80
@@ -15353,14 +15353,14 @@
-1FC0
-30C0
-60C0
-60C0
-60C0
-60C0
-60C0
-60C0
-30C0
-1FC0
-00C0
-00C0
-0180
-3F00
+1F80
+3180
+6180
+6180
+6180
+6180
+6180
+6180
+6180
+FFC0
+C0C0
+C0C0
+0000
+0000
--- ./ter-u22n.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22n.bdf 2020-12-28 20:34:21.915123000 +0200
@@ -15291,8 +15291,6 @@
-3C00
-4200
-4100
-4100
-4100
-4200
-7E00
-4100
+0000
+0000
+0000
+0000
+7F00
+4080
@@ -15300,0 +15299 @@
+7F00
@@ -15303,2 +15302,3 @@
-4100
-7E00
+4080
+4080
+7F00
@@ -15353 +15353,4 @@
-1F80
+0F80
+1080
+2080
+2080
@@ -15355,6 +15357,0 @@
-4080
-4080
-4080
-4080
-4080
-4080
@@ -15362,5 +15359,8 @@
-1F80
-0080
-0080
-0100
-3E00
+2080
+2080
+2080
+7FC0
+4040
+4040
+0000
+0000
--- ./ter-u24b.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24b.bdf 2020-12-28 20:34:21.955123000 +0200
@@ -16344,2 +16344,5 @@
-3F00
-6180
+0000
+0000
+0000
+0000
+7F80
@@ -16349,2 +16351,0 @@
-6180
-7F80
@@ -16351,0 +16353 @@
+7FC0
@@ -16356,3 +16358 @@
-6060
-60C0
-7F80
+7FC0
@@ -16410,7 +16410,11 @@
-1FE0
-3060
-6060
-6060
-6060
-6060
-6060
+0FC0
+18C0
+30C0
+30C0
+30C0
+30C0
+30C0
+30C0
+30C0
+30C0
+7FE0
@@ -16419,6 +16423,2 @@
-30E0
-1FE0
-0060
-0060
-00C0
-3F80
+0000
+0000
--- ./ter-u24n.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24n.bdf 2020-12-28 20:34:21.915123000 +0200
@@ -16344,2 +16344,5 @@
-3E00
-4100
+0000
+0000
+0000
+0000
+7F00
@@ -16349,2 +16351,0 @@
-4100
-7F00
@@ -16351,0 +16353 @@
+7F80
@@ -16356,3 +16358 @@
-4040
-4080
-7F00
+7F80
@@ -16410,7 +16410,11 @@
-1FC0
-2040
-4040
-4040
-4040
-4040
-4040
+0F80
+1080
+2080
+2080
+2080
+2080
+2080
+2080
+2080
+2080
+7FC0
@@ -16419,6 +16423,2 @@
-20C0
-1F40
-0040
-0040
-0080
-3F00
+0000
+0000
--- ./ter-u28b.bdf.orig 2020-12-28 20:34:18.845118700 +0200
+++ ./ter-u28b.bdf 2020-12-28 20:34:21.975123000 +0200
@@ -18448 +18448,5 @@
-3F00
+0000
+0000
+0000
+0000
+0000
@@ -18450,5 +18453,0 @@
-61C0
-60C0
-60C0
-60C0
-6180
@@ -18456,4 +18455,6 @@
-7FE0
-6070
-6030
-6030
+6060
+6060
+6060
+7FC0
+7FC0
+6060
@@ -18463 +18463,0 @@
-6070
@@ -18523 +18523,11 @@
-1FF0
+07F0
+0FF0
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
@@ -18525,16 +18535,6 @@
-7030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-7030
-3FF0
-1FF0
-0030
-0030
-0070
-3FE0
-3FC0
+7FF8
+6018
+6018
+6018
+0000
+0000
--- ./ter-u28n.bdf.orig 2020-12-28 20:34:18.835118600 +0200
+++ ./ter-u28n.bdf 2020-12-28 20:34:21.965123000 +0200
@@ -18448,4 +18448,6 @@
-3F00
-6180
-60C0
-60C0
+0000
+0000
+0000
+0000
+0000
+7F80
@@ -18452,0 +18455,3 @@
+6060
+6060
+6060
@@ -18454 +18458,0 @@
-6180
@@ -18460,4 +18463,0 @@
-6030
-6030
-6030
-6030
@@ -18523,18 +18523,18 @@
-1FF0
-3030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-3030
-1FF0
-0030
-0030
-0030
-0060
-3FC0
+07F0
+0C30
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+7FF8
+6018
+6018
+6018
+0000
+0000
--- ./ter-u32b.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32b.bdf 2020-12-28 20:34:22.025123100 +0200
@@ -20554,8 +20554,6 @@
-3FC0
-7FE0
-70F0
-7070
-7070
-7070
-7070
-70E0
+0000
+0000
+0000
+0000
+0000
+0000
@@ -20563,0 +20562 @@
+7078
@@ -20565,4 +20564,5 @@
-701C
-701C
-701C
-701C
+7038
+7038
+7FF0
+7FF8
+703C
@@ -20638 +20638,12 @@
-1FFC
+07FC
+0FFC
+1E1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
@@ -20640,17 +20651,6 @@
-781C
-701C
-701C
-701C
-701C
-701C
-701C
-701C
-701C
-781C
-3FFC
-1FFC
-001C
-001C
-003C
-3FF8
-3FF0
+7FFE
+700E
+700E
+700E
+700E
+0000
--- ./ter-u32n.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32n.bdf 2020-12-28 20:34:21.985123000 +0200
@@ -20554,8 +20554,6 @@
-1FC0
-3FE0
-3070
-3030
-3030
-3030
-3030
-3070
+0000
+0000
+0000
+0000
+0000
+0000
@@ -20564,0 +20563,5 @@
+3018
+3018
+3038
+3FF0
+3FF8
@@ -20568,3 +20570,0 @@
-300C
-300C
-300C
@@ -20638,19 +20638,19 @@
-0FFC
-1FFC
-380C
-300C
-300C
-300C
-300C
-300C
-300C
-300C
-300C
-380C
-1FFC
-0FFC
-000C
-000C
-001C
-1FF8
-1FF0
+07F8
+0FF8
+1C18
+1818
+1818
+1818
+1818
+1818
+1818
+1818
+1818
+1818
+3FF8
+7FFC
+600C
+600C
+600C
+600C
+0000
+992
View File
@@ -0,0 +1,992 @@
--- ./ter-u12b.bdf.orig 2020-12-28 20:34:18.635118400 +0200
+++ ./ter-u12b.bdf 2020-12-28 20:34:22.635124000 +0200
@@ -10051,4 +10051,3 @@
-F8
-80
-80
-80
+70
+08
+70
@@ -10056,0 +10056 @@
+70
@@ -10659,4 +10659,3 @@
-F8
-80
-80
-80
+70
+08
+70
@@ -10664,0 +10664 @@
+70
@@ -10998,0 +10999,3 @@
+40
+40
+70
@@ -11000,5 +11003 @@
-08
-F8
-80
-80
-80
+70
@@ -11006,0 +11006 @@
+70
--- ./ter-u12n.bdf.orig 2020-12-28 20:34:18.605118300 +0200
+++ ./ter-u12n.bdf 2020-12-28 20:34:22.625123900 +0200
@@ -10051,4 +10051,3 @@
-F8
-80
-80
-80
+70
+08
+70
@@ -10056,0 +10056 @@
+70
@@ -10659,4 +10659,3 @@
-F8
-80
-80
-80
+70
+08
+70
@@ -10664,0 +10664 @@
+70
@@ -10998,0 +10999,3 @@
+40
+40
+70
@@ -11000,5 +11003 @@
-08
-F8
-80
-80
-80
+70
@@ -11006,0 +11006 @@
+70
--- ./ter-u14b.bdf.orig 2020-12-28 20:34:18.655118400 +0200
+++ ./ter-u14b.bdf 2020-12-28 20:34:22.675124000 +0200
@@ -11106,5 +11106,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -11112,0 +11112 @@
+7C
@@ -11778,5 +11778,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -11784,0 +11784 @@
+7C
@@ -12153,0 +12154,3 @@
+60
+60
+7C
@@ -12156,5 +12159 @@
-FE
-C0
-C0
-C0
-C0
+7C
@@ -12162,0 +12162 @@
+7C
--- ./ter-u14n.bdf.orig 2020-12-28 20:34:18.645118400 +0200
+++ ./ter-u14n.bdf 2020-12-28 20:34:22.655124000 +0200
@@ -11106,5 +11106,4 @@
-7E
-40
-40
-40
-40
+3C
+02
+02
+3C
@@ -11112,0 +11112 @@
+3C
@@ -11778,5 +11778,4 @@
-7E
-40
-40
-40
-40
+3C
+02
+02
+3C
@@ -11784,0 +11784 @@
+3C
@@ -12153,0 +12154,3 @@
+20
+20
+3C
@@ -12156,5 +12159 @@
-7E
-40
-40
-40
-40
+3C
@@ -12162,0 +12162 @@
+3C
--- ./ter-u14v.bdf.orig 2020-12-28 20:34:18.675118400 +0200
+++ ./ter-u14v.bdf 2020-12-28 20:34:22.665124000 +0200
@@ -11106,5 +11106,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -11112,0 +11112 @@
+7C
@@ -11778,5 +11778,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -11784,0 +11784 @@
+7C
@@ -12153,0 +12154,3 @@
+60
+60
+7C
@@ -12156,5 +12159 @@
-FE
-C0
-C0
-C0
-C0
+7C
@@ -12162,0 +12162 @@
+7C
--- ./ter-u16b.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16b.bdf 2020-12-28 20:34:22.695124000 +0200
@@ -12160,5 +12160,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -12166,0 +12166 @@
+7C
@@ -12896,5 +12896,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -12902,0 +12902 @@
+7C
@@ -13307,0 +13308,3 @@
+60
+60
+7C
@@ -13310,5 +13313 @@
-FE
-C0
-C0
-C0
-C0
+7C
@@ -13316,0 +13316 @@
+7C
--- ./ter-u16n.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16n.bdf 2020-12-28 20:34:22.685124000 +0200
@@ -12160,5 +12160,4 @@
-7E
-40
-40
-40
-40
+3C
+02
+02
+3C
@@ -12166,0 +12166 @@
+3C
@@ -12896,5 +12896,4 @@
-7E
-40
-40
-40
-40
+3C
+02
+02
+3C
@@ -12902,0 +12902 @@
+3C
@@ -13307,0 +13308,3 @@
+20
+20
+3C
@@ -13310,5 +13313 @@
-7E
-40
-40
-40
-40
+3C
@@ -13316,0 +13316 @@
+3C
--- ./ter-u16v.bdf.orig 2020-12-28 20:34:18.695118400 +0200
+++ ./ter-u16v.bdf 2020-12-28 20:34:22.715124100 +0200
@@ -12160,5 +12160,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -12166,0 +12166 @@
+7C
@@ -12896,5 +12896,4 @@
-FE
-C0
-C0
-C0
-C0
+7C
+06
+06
+7C
@@ -12902,0 +12902 @@
+7C
@@ -13307,0 +13308,3 @@
+60
+60
+7C
@@ -13310,5 +13313 @@
-FE
-C0
-C0
-C0
-C0
+7C
@@ -13316,0 +13316 @@
+7C
--- ./ter-u18b.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18b.bdf 2020-12-28 20:34:22.725124100 +0200
@@ -13215,6 +13215,5 @@
-7F80
-6000
-6000
-6000
-6000
-6000
+3F00
+0180
+0180
+0180
+3F00
@@ -13223,0 +13223 @@
+3F00
@@ -14015,6 +14015,5 @@
-7F80
-6000
-6000
-6000
-6000
-6000
+3F00
+0180
+0180
+0180
+3F00
@@ -14023,0 +14023 @@
+3F00
@@ -14462,0 +14463,3 @@
+3000
+3000
+3F00
@@ -14465,6 +14468,2 @@
-7F80
-6000
-6000
-6000
-6000
-6000
+0180
+3F00
@@ -14473,0 +14473 @@
+3F00
--- ./ter-u18n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18n.bdf 2020-12-28 20:34:22.715124100 +0200
@@ -13215,6 +13215,5 @@
-7F00
-4000
-4000
-4000
-4000
-4000
+3E00
+0100
+0100
+0100
+3E00
@@ -13223,0 +13223 @@
+3E00
@@ -14015,6 +14015,5 @@
-7F00
-4000
-4000
-4000
-4000
-4000
+3E00
+0100
+0100
+0100
+3E00
@@ -14023,0 +14023 @@
+3E00
@@ -14462,0 +14463,3 @@
+2000
+2000
+3E00
@@ -14465,6 +14468,2 @@
-7F00
-4000
-4000
-4000
-4000
-4000
+0100
+3E00
@@ -14473,0 +14473 @@
+3E00
--- ./ter-u20b.bdf.orig 2020-12-28 20:34:18.735118500 +0200
+++ ./ter-u20b.bdf 2020-12-28 20:34:22.775124200 +0200
@@ -14270,6 +14270,5 @@
-7F80
-6000
-6000
-6000
-6000
-6000
+3F00
+0180
+0180
+0180
+3F00
@@ -14278,0 +14278 @@
+3F00
@@ -15134,6 +15134,5 @@
-7F80
-6000
-6000
-6000
-6000
-6000
+3F00
+0180
+0180
+0180
+3F00
@@ -15142,0 +15142 @@
+3F00
@@ -15617,0 +15618,3 @@
+3000
+3000
+3F00
@@ -15620,6 +15623,2 @@
-7F80
-6000
-6000
-6000
-6000
-6000
+0180
+3F00
@@ -15628,0 +15628 @@
+3F00
--- ./ter-u20n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u20n.bdf 2020-12-28 20:34:22.735124100 +0200
@@ -14270,6 +14270,5 @@
-7F00
-4000
-4000
-4000
-4000
-4000
+3E00
+0100
+0100
+0100
+3E00
@@ -14278,0 +14278 @@
+3E00
@@ -15134,6 +15134,5 @@
-7F00
-4000
-4000
-4000
-4000
-4000
+3E00
+0100
+0100
+0100
+3E00
@@ -15142,0 +15142 @@
+3E00
@@ -15617,0 +15618,3 @@
+2000
+2000
+3E00
@@ -15620,6 +15623,2 @@
-7F00
-4000
-4000
-4000
-4000
-4000
+0100
+3E00
@@ -15628,0 +15628 @@
+3E00
--- ./ter-u22b.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22b.bdf 2020-12-28 20:34:22.775124200 +0200
@@ -15324,6 +15324,5 @@
-7FC0
-6000
-6000
-6000
-6000
-6000
+3F80
+00C0
+00C0
+00C0
+3F80
@@ -15333,0 +15333 @@
+3F80
@@ -16252,6 +16252,5 @@
-7FC0
-6000
-6000
-6000
-6000
-6000
+3F80
+00C0
+00C0
+00C0
+3F80
@@ -16261,0 +16261 @@
+3F80
@@ -16770,0 +16771,4 @@
+3000
+3000
+3000
+3F80
@@ -16774,6 +16778 @@
-7FC0
-6000
-6000
-6000
-6000
-6000
+3F80
@@ -16783,0 +16783 @@
+3F80
--- ./ter-u22n.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22n.bdf 2020-12-28 20:34:22.775124200 +0200
@@ -15324,6 +15324,5 @@
-7F80
-4000
-4000
-4000
-4000
-4000
+3F00
+0080
+0080
+0080
+3F00
@@ -15333,0 +15333 @@
+3F00
@@ -16252,6 +16252,5 @@
-7F80
-4000
-4000
-4000
-4000
-4000
+3F00
+0080
+0080
+0080
+3F00
@@ -16261,0 +16261 @@
+3F00
@@ -16770,0 +16771,4 @@
+2000
+2000
+2000
+3F00
@@ -16774,6 +16778 @@
-7F80
-4000
-4000
-4000
-4000
-4000
+3F00
@@ -16783,0 +16783 @@
+3F00
--- ./ter-u24b.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24b.bdf 2020-12-28 20:34:22.825124200 +0200
@@ -16379,7 +16379,6 @@
-7FE0
-6000
-6000
-6000
-6000
-6000
-6000
+3FC0
+0060
+0060
+0060
+0060
+3FC0
@@ -16389,0 +16389 @@
+3FC0
@@ -17371,7 +17371,6 @@
-7FE0
-6000
-6000
-6000
-6000
-6000
-6000
+3FC0
+0060
+0060
+0060
+0060
+3FC0
@@ -17381,0 +17381 @@
+3FC0
@@ -17925,0 +17926,4 @@
+3000
+3000
+3000
+3FC0
@@ -17929,7 +17933,2 @@
-7FE0
-6000
-6000
-6000
-6000
-6000
-6000
+0060
+3FC0
@@ -17939,0 +17939 @@
+3FC0
--- ./ter-u24n.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24n.bdf 2020-12-28 20:34:22.775124200 +0200
@@ -16379,7 +16379,6 @@
-7FC0
-4000
-4000
-4000
-4000
-4000
-4000
+3F80
+0040
+0040
+0040
+0040
+3F80
@@ -16389,0 +16389 @@
+3F80
@@ -17371,7 +17371,6 @@
-7FC0
-4000
-4000
-4000
-4000
-4000
-4000
+3F80
+0040
+0040
+0040
+0040
+3F80
@@ -17381,0 +17381 @@
+3F80
@@ -17925,0 +17926,4 @@
+2000
+2000
+2000
+3F80
@@ -17929,7 +17933,2 @@
-7FC0
-4000
-4000
-4000
-4000
-4000
-4000
+0040
+3F80
@@ -17939,0 +17939 @@
+3F80
--- ./ter-u28b.bdf.orig 2020-12-28 20:34:18.845118700 +0200
+++ ./ter-u28b.bdf 2020-12-28 20:34:22.845124300 +0200
@@ -18488,11 +18488,8 @@
-7FF0
-7FF0
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
+3FC0
+3FE0
+0070
+0030
+0030
+1FE0
+3FC0
+7000
@@ -18500,0 +18498,3 @@
+7000
+3FE0
+1FE0
@@ -19608,11 +19608,8 @@
-7FF0
-7FF0
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
+3FC0
+3FE0
+0070
+0030
+0030
+1FE0
+3FC0
+7000
@@ -19620,0 +19618,3 @@
+7000
+3FE0
+1FE0
@@ -20234,0 +20235,6 @@
+3000
+3000
+3000
+3FC0
+3FE0
+0070
@@ -20237,12 +20243,3 @@
-0030
-7FF0
-7FF0
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
+1FE0
+3FC0
+7000
@@ -20250,0 +20248,3 @@
+7000
+3FE0
+1FE0
--- ./ter-u28n.bdf.orig 2020-12-28 20:34:18.835118600 +0200
+++ ./ter-u28n.bdf 2020-12-28 20:34:22.835124200 +0200
@@ -18488,10 +18488,8 @@
-7FF0
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
+3FC0
+0060
+0030
+0030
+0030
+0060
+1FC0
+3000
@@ -18500,0 +18499,2 @@
+3000
+1FE0
@@ -19608,10 +19608,8 @@
-7FF0
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
+3FC0
+0060
+0030
+0030
+0030
+0060
+1FC0
+3000
@@ -19620,0 +19619,2 @@
+3000
+1FE0
@@ -20234,0 +20235,5 @@
+3000
+3000
+3000
+3FC0
+0060
@@ -20238,10 +20243,3 @@
-7FF0
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
-6000
+0060
+1FC0
+3000
@@ -20250,0 +20249,2 @@
+3000
+1FE0
--- ./ter-u32b.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32b.bdf 2020-12-28 20:34:22.905124300 +0200
@@ -20599,12 +20599,9 @@
-7FFC
-7FFC
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
+3FF0
+3FF8
+003C
+001C
+001C
+003C
+1FF8
+3FF0
+7800
@@ -20612,0 +20610,3 @@
+7800
+3FF8
+1FF8
@@ -21847,12 +21847,9 @@
-7FFC
-7FFC
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
+3FF0
+3FF8
+003C
+001C
+001C
+003C
+1FF8
+3FF0
+7800
@@ -21860,0 +21858,3 @@
+7800
+3FF8
+1FF8
@@ -22544,0 +22545,7 @@
+3800
+3800
+3800
+3800
+3FF0
+3FF8
+003C
@@ -22547,14 +22554,4 @@
-001C
-001C
-7FFC
-7FFC
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
-7000
+003C
+1FF8
+3FF0
+7800
@@ -22562,0 +22560,3 @@
+7800
+3FF8
+1FF8
--- ./ter-u32n.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32n.bdf 2020-12-28 20:34:22.855124300 +0200
@@ -20599,12 +20599,9 @@
-3FFC
-3FFC
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
+1FF0
+1FF8
+001C
+000C
+000C
+001C
+0FF8
+1FF0
+3800
@@ -20612,0 +20610,3 @@
+3800
+1FF8
+0FF8
@@ -21847,12 +21847,9 @@
-3FFC
-3FFC
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
+1FF0
+1FF8
+001C
+000C
+000C
+001C
+0FF8
+1FF0
+3800
@@ -21860,0 +21858,3 @@
+3800
+1FF8
+0FF8
@@ -22544,0 +22545,7 @@
+1800
+1800
+1800
+1800
+1FF0
+1FF8
+001C
@@ -22547,14 +22554,4 @@
-000C
-000C
-3FFC
-3FFC
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
-3000
+001C
+0FF8
+1FF0
+3800
@@ -22562,0 +22560,3 @@
+3800
+1FF8
+0FF8
+304
View File
@@ -0,0 +1,304 @@
--- ./ter-u12b.bdf.orig 2020-12-28 20:34:18.635118400 +0200
+++ ./ter-u12b.bdf 2020-12-28 20:34:23.545125200 +0200
@@ -189 +189 @@
-20
+40
@@ -1269,3 +1268,0 @@
-40
-20
-00
@@ -1272,0 +1270,3 @@
+20
+20
+10
--- ./ter-u12n.bdf.orig 2020-12-28 20:34:18.605118300 +0200
+++ ./ter-u12n.bdf 2020-12-28 20:34:23.535125200 +0200
@@ -189 +189 @@
-20
+40
@@ -1269,3 +1268,0 @@
-40
-20
-00
@@ -1272,0 +1270,3 @@
+20
+20
+10
--- ./ter-u14b.bdf.orig 2020-12-28 20:34:18.655118400 +0200
+++ ./ter-u14b.bdf 2020-12-28 20:34:23.575125300 +0200
@@ -205 +205 @@
-18
+30
@@ -1398,0 +1399,2 @@
+00
+30
@@ -1401,2 +1402,0 @@
-00
-00
--- ./ter-u14n.bdf.orig 2020-12-28 20:34:18.645118400 +0200
+++ ./ter-u14n.bdf 2020-12-28 20:34:23.555125200 +0200
@@ -203,2 +203,2 @@
-10
-10
+08
+08
@@ -1398,0 +1399,2 @@
+00
+10
@@ -1401,2 +1402,0 @@
-00
-00
--- ./ter-u14v.bdf.orig 2020-12-28 20:34:18.675118400 +0200
+++ ./ter-u14v.bdf 2020-12-28 20:34:23.575125300 +0200
@@ -205 +205 @@
-18
+30
@@ -1398,0 +1399,2 @@
+00
+30
@@ -1401,2 +1402,0 @@
-00
-00
--- ./ter-u16b.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16b.bdf 2020-12-28 20:34:23.595125300 +0200
@@ -221 +221 @@
-18
+30
@@ -1528,0 +1529,2 @@
+00
+30
@@ -1531,2 +1532,0 @@
-00
-00
--- ./ter-u16n.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16n.bdf 2020-12-28 20:34:23.585125300 +0200
@@ -219,2 +219,2 @@
-10
-10
+08
+08
@@ -1528,0 +1529,2 @@
+00
+10
@@ -1531,2 +1532,0 @@
-00
-00
--- ./ter-u16v.bdf.orig 2020-12-28 20:34:18.695118400 +0200
+++ ./ter-u16v.bdf 2020-12-28 20:34:23.615125300 +0200
@@ -221 +221 @@
-18
+30
@@ -1528,0 +1529,2 @@
+00
+30
@@ -1531,2 +1532,0 @@
-00
-00
--- ./ter-u18b.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18b.bdf 2020-12-28 20:34:23.635125400 +0200
@@ -238 +238 @@
-0C00
+1800
@@ -1659 +1659,3 @@
-3000
+0000
+1800
+1800
@@ -1662,2 +1663,0 @@
-0000
-0000
--- ./ter-u18n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18n.bdf 2020-12-28 20:34:23.625125300 +0200
@@ -238 +238 @@
-0800
+1000
@@ -1659 +1659,3 @@
-2000
+0000
+1000
+1000
@@ -1662,2 +1663,0 @@
-0000
-0000
--- ./ter-u20b.bdf.orig 2020-12-28 20:34:18.735118500 +0200
+++ ./ter-u20b.bdf 2020-12-28 20:34:23.665125400 +0200
@@ -254 +254 @@
-0C00
+1800
@@ -1789 +1789,3 @@
-3000
+0000
+1800
+1800
@@ -1792,2 +1793,0 @@
-0000
-0000
--- ./ter-u20n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u20n.bdf 2020-12-28 20:34:23.635125400 +0200
@@ -254 +254 @@
-0800
+1000
@@ -1789 +1789,3 @@
-2000
+0000
+1000
+1000
@@ -1792,2 +1793,0 @@
-0000
-0000
--- ./ter-u22b.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22b.bdf 2020-12-28 20:34:23.675125400 +0200
@@ -267,4 +267,4 @@
-0C00
-0C00
-0C00
-0C00
+0600
+0600
+0600
+0600
@@ -1919 +1919,4 @@
-1800
+0000
+0C00
+0C00
+0C00
@@ -1922,3 +1924,0 @@
-0000
-0000
-0000
--- ./ter-u22n.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22n.bdf 2020-12-28 20:34:23.675125400 +0200
@@ -271 +271 @@
-0400
+0800
@@ -1919,5 +1918,0 @@
-1000
-0800
-0400
-0000
-0000
@@ -1924,0 +1920,5 @@
+0400
+0400
+0400
+0400
+0200
--- ./ter-u24b.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24b.bdf 2020-12-28 20:34:23.715125500 +0200
@@ -288 +288 @@
-0600
+0C00
@@ -2050,5 +2049,0 @@
-1800
-0C00
-0600
-0000
-0000
@@ -2055,0 +2051,5 @@
+0600
+0600
+0600
+0600
+0300
--- ./ter-u24n.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24n.bdf 2020-12-28 20:34:23.685125400 +0200
@@ -288 +288 @@
-0400
+0800
@@ -2050,5 +2049,0 @@
-1000
-0800
-0400
-0000
-0000
@@ -2055,0 +2051,5 @@
+0400
+0400
+0400
+0400
+0200
--- ./ter-u28b.bdf.orig 2020-12-28 20:34:18.845118700 +0200
+++ ./ter-u28b.bdf 2020-12-28 20:34:23.735125500 +0200
@@ -320,2 +320,2 @@
-0300
-0300
+0600
+0600
@@ -2309,6 +2308,0 @@
-3800
-1C00
-0E00
-0700
-0000
-0000
@@ -2316,0 +2311,6 @@
+0600
+0600
+0600
+0600
+0300
+0300
--- ./ter-u28n.bdf.orig 2020-12-28 20:34:18.835118600 +0200
+++ ./ter-u28n.bdf 2020-12-28 20:34:23.735125500 +0200
@@ -320,2 +320,2 @@
-0300
-0300
+0600
+0600
@@ -2309,6 +2308,0 @@
-1800
-0C00
-0600
-0300
-0000
-0000
@@ -2316,0 +2311,6 @@
+0600
+0600
+0600
+0600
+0300
+0300
--- ./ter-u32b.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32b.bdf 2020-12-28 20:34:23.775125600 +0200
@@ -354,2 +354,2 @@
-0380
-0380
+0700
+0700
@@ -2571,6 +2570,0 @@
-1C00
-0E00
-0700
-0380
-0000
-0000
@@ -2578,0 +2573,6 @@
+0380
+0380
+0380
+0380
+01C0
+01C0
--- ./ter-u32n.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32n.bdf 2020-12-28 20:34:23.765125500 +0200
@@ -354,2 +354,2 @@
-0180
-0180
+0300
+0300
@@ -2571,6 +2570,0 @@
-0E00
-0700
-0380
-01C0
-0000
-0000
@@ -2578,0 +2573,6 @@
+0180
+0180
+0180
+0180
+00C0
+00C0
+860
View File
@@ -0,0 +1,860 @@
--- ./ter-u12b.bdf.orig 2020-12-28 20:34:27.995131500 +0200
+++ ./ter-u12b.bdf 2020-12-28 20:34:29.445133500 +0200
@@ -10030,3 +10030,2 @@
-E0
-90
-90
+00
+00
@@ -10034,0 +10034 @@
+F0
@@ -10070,8 +10070,8 @@
-78
-88
-88
-88
-88
-78
-08
-70
+38
+48
+48
+48
+48
+FC
+84
+00
--- ./ter-u12n.bdf.orig 2020-12-28 20:34:27.975131400 +0200
+++ ./ter-u12n.bdf 2020-12-28 20:34:29.425133500 +0200
@@ -10030,3 +10030,2 @@
-E0
-90
-90
+00
+00
@@ -10034,0 +10034 @@
+F0
@@ -10070,8 +10070,8 @@
-78
-88
-88
-88
-88
-78
-08
-70
+38
+48
+48
+48
+48
+FC
+84
+00
--- ./ter-u14b.bdf.orig 2020-12-28 20:34:28.025131500 +0200
+++ ./ter-u14b.bdf 2020-12-28 20:34:29.465133500 +0200
@@ -11082,4 +11082,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -11088,0 +11088 @@
+FC
@@ -11127,9 +11127,9 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
--- ./ter-u14n.bdf.orig 2020-12-28 20:34:27.995131500 +0200
+++ ./ter-u14n.bdf 2020-12-28 20:34:29.465133500 +0200
@@ -11082,4 +11082,3 @@
-38
-44
-44
-48
+00
+00
+00
@@ -11088,0 +11088 @@
+7C
@@ -11127,8 +11126,0 @@
-3E
-42
-42
-42
-42
-42
-3E
-02
@@ -11135,0 +11128,8 @@
+44
+44
+44
+44
+44
+FE
+82
+00
--- ./ter-u14v.bdf.orig 2020-12-28 20:34:28.025131500 +0200
+++ ./ter-u14v.bdf 2020-12-28 20:34:29.475133500 +0200
@@ -11082,4 +11082,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -11088,0 +11088 @@
+FC
@@ -11127,9 +11127,9 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
--- ./ter-u16b.bdf.orig 2020-12-28 20:34:28.035131500 +0200
+++ ./ter-u16b.bdf 2020-12-28 20:34:29.515133600 +0200
@@ -12134,4 +12134,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -12140,0 +12140 @@
+FC
@@ -12183,10 +12183,10 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
+00
--- ./ter-u16n.bdf.orig 2020-12-28 20:34:28.035131500 +0200
+++ ./ter-u16n.bdf 2020-12-28 20:34:29.485133500 +0200
@@ -12134,4 +12134,3 @@
-38
-44
-44
-48
+00
+00
+00
@@ -12140,0 +12140 @@
+7C
@@ -12183,9 +12182,0 @@
-3E
-42
-42
-42
-42
-42
-3E
-02
-02
@@ -12192,0 +12184,9 @@
+44
+44
+44
+44
+44
+FE
+82
+00
+00
--- ./ter-u16v.bdf.orig 2020-12-28 20:34:28.065131600 +0200
+++ ./ter-u16v.bdf 2020-12-28 20:34:29.515133600 +0200
@@ -12134,4 +12134,3 @@
-78
-CC
-CC
-C8
+00
+00
+00
@@ -12140,0 +12140 @@
+FC
@@ -12183,10 +12183,10 @@
-7E
-C6
-C6
-C6
-C6
-C6
-7E
-06
-06
-7C
+3E
+66
+66
+66
+66
+66
+FF
+C3
+00
+00
--- ./ter-u18b.bdf.orig 2020-12-28 20:34:28.085131600 +0200
+++ ./ter-u18b.bdf 2020-12-28 20:34:29.535133600 +0200
@@ -13186,5 +13186,4 @@
-3E00
-6300
-6300
-6300
-6200
+0000
+0000
+0000
+0000
@@ -13194,0 +13194 @@
+7F00
@@ -13240,12 +13240,12 @@
-3F80
-6180
-6180
-6180
-6180
-6180
-6180
-6180
-3F80
-0180
-0180
-3F00
+1F00
+3300
+6300
+6300
+6300
+6300
+6300
+6300
+FF80
+C180
+C180
+0000
--- ./ter-u18n.bdf.orig 2020-12-28 20:34:28.065131600 +0200
+++ ./ter-u18n.bdf 2020-12-28 20:34:29.515133600 +0200
@@ -13186,5 +13186,4 @@
-3C00
-4200
-4200
-4200
-4400
+0000
+0000
+0000
+0000
@@ -13194,0 +13194 @@
+7E00
@@ -13240,12 +13240,12 @@
-3F00
-4100
-4100
-4100
-4100
-4100
-4100
-4100
-3F00
-0100
-0100
-3E00
+0F00
+1100
+2100
+2100
+2100
+2100
+2100
+2100
+7F80
+4080
+4080
+0000
--- ./ter-u20b.bdf.orig 2020-12-28 20:34:28.115131600 +0200
+++ ./ter-u20b.bdf 2020-12-28 20:34:29.575133700 +0200
@@ -14239,5 +14239,4 @@
-3E00
-6300
-6300
-6300
-6200
+0000
+0000
+0000
+0000
@@ -14247,0 +14247 @@
+7F00
@@ -14297,12 +14297,12 @@
-3F80
-6180
-6180
-6180
-6180
-6180
-6180
-6180
-3F80
-0180
-0180
-3F00
+1F00
+3300
+6300
+6300
+6300
+6300
+6300
+6300
+FF80
+C180
+C180
+0000
--- ./ter-u20n.bdf.orig 2020-12-28 20:34:28.085131600 +0200
+++ ./ter-u20n.bdf 2020-12-28 20:34:29.565133700 +0200
@@ -14239,5 +14239,4 @@
-3C00
-4200
-4200
-4200
-4400
+0000
+0000
+0000
+0000
@@ -14247,0 +14247 @@
+7E00
@@ -14297,12 +14297,12 @@
-3F00
-4100
-4100
-4100
-4100
-4100
-4100
-4100
-3F00
-0100
-0100
-3E00
+0F00
+1100
+2100
+2100
+2100
+2100
+2100
+2100
+7F80
+4080
+4080
+0000
--- ./ter-u22b.bdf.orig 2020-12-28 20:34:28.135131700 +0200
+++ ./ter-u22b.bdf 2020-12-28 20:34:29.605133700 +0200
@@ -15291,8 +15291,5 @@
-3E00
-6300
-6180
-6180
-6180
-6300
-7F00
-6180
+0000
+0000
+0000
+0000
+7F80
@@ -15301,0 +15299 @@
+7F80
@@ -15303,2 +15301,4 @@
-6180
-7F00
+60C0
+60C0
+60C0
+7F80
@@ -15353,14 +15353,14 @@
-1FC0
-30C0
-60C0
-60C0
-60C0
-60C0
-60C0
-60C0
-30C0
-1FC0
-00C0
-00C0
-0180
-3F00
+1F80
+3180
+6180
+6180
+6180
+6180
+6180
+6180
+6180
+FFC0
+C0C0
+C0C0
+0000
+0000
--- ./ter-u22n.bdf.orig 2020-12-28 20:34:28.115131600 +0200
+++ ./ter-u22n.bdf 2020-12-28 20:34:29.575133700 +0200
@@ -15291,8 +15291,6 @@
-3C00
-4200
-4100
-4100
-4100
-4200
-7E00
-4100
+0000
+0000
+0000
+0000
+7F00
+4080
@@ -15300,0 +15299 @@
+7F00
@@ -15303,2 +15302,3 @@
-4100
-7E00
+4080
+4080
+7F00
@@ -15353 +15353,4 @@
-1F80
+0F80
+1080
+2080
+2080
@@ -15355,6 +15357,0 @@
-4080
-4080
-4080
-4080
-4080
-4080
@@ -15362,5 +15359,8 @@
-1F80
-0080
-0080
-0100
-3E00
+2080
+2080
+2080
+7FC0
+4040
+4040
+0000
+0000
--- ./ter-u24b.bdf.orig 2020-12-28 20:34:28.165131700 +0200
+++ ./ter-u24b.bdf 2020-12-28 20:34:29.615133700 +0200
@@ -16344,2 +16344,5 @@
-3F00
-6180
+0000
+0000
+0000
+0000
+7F80
@@ -16349,2 +16351,0 @@
-6180
-7F80
@@ -16351,0 +16353 @@
+7FC0
@@ -16356,3 +16358 @@
-6060
-60C0
-7F80
+7FC0
@@ -16410,7 +16410,11 @@
-1FE0
-3060
-6060
-6060
-6060
-6060
-6060
+0FC0
+18C0
+30C0
+30C0
+30C0
+30C0
+30C0
+30C0
+30C0
+30C0
+7FE0
@@ -16419,6 +16423,2 @@
-30E0
-1FE0
-0060
-0060
-00C0
-3F80
+0000
+0000
--- ./ter-u24n.bdf.orig 2020-12-28 20:34:28.135131700 +0200
+++ ./ter-u24n.bdf 2020-12-28 20:34:29.625133700 +0200
@@ -16344,2 +16344,5 @@
-3E00
-4100
+0000
+0000
+0000
+0000
+7F00
@@ -16349,2 +16351,0 @@
-4100
-7F00
@@ -16351,0 +16353 @@
+7F80
@@ -16356,3 +16358 @@
-4040
-4080
-7F00
+7F80
@@ -16410,7 +16410,11 @@
-1FC0
-2040
-4040
-4040
-4040
-4040
-4040
+0F80
+1080
+2080
+2080
+2080
+2080
+2080
+2080
+2080
+2080
+7FC0
@@ -16419,6 +16423,2 @@
-20C0
-1F40
-0040
-0040
-0080
-3F00
+0000
+0000
--- ./ter-u28b.bdf.orig 2020-12-28 20:34:28.185131700 +0200
+++ ./ter-u28b.bdf 2020-12-28 20:34:29.665133800 +0200
@@ -18448 +18448,5 @@
-3F00
+0000
+0000
+0000
+0000
+0000
@@ -18450,5 +18453,0 @@
-61C0
-60C0
-60C0
-60C0
-6180
@@ -18456,4 +18455,6 @@
-7FE0
-6070
-6030
-6030
+6060
+6060
+6060
+7FC0
+7FC0
+6060
@@ -18463 +18463,0 @@
-6070
@@ -18523 +18523,11 @@
-1FF0
+07F0
+0FF0
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
@@ -18525,16 +18535,6 @@
-7030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-7030
-3FF0
-1FF0
-0030
-0030
-0070
-3FE0
-3FC0
+7FF8
+6018
+6018
+6018
+0000
+0000
--- ./ter-u28n.bdf.orig 2020-12-28 20:34:28.175131700 +0200
+++ ./ter-u28n.bdf 2020-12-28 20:34:29.635133800 +0200
@@ -18448,4 +18448,6 @@
-3F00
-6180
-60C0
-60C0
+0000
+0000
+0000
+0000
+0000
+7F80
@@ -18452,0 +18455,3 @@
+6060
+6060
+6060
@@ -18454 +18458,0 @@
-6180
@@ -18460,4 +18463,0 @@
-6030
-6030
-6030
-6030
@@ -18523,18 +18523,18 @@
-1FF0
-3030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-6030
-3030
-1FF0
-0030
-0030
-0030
-0060
-3FC0
+07F0
+0C30
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+1830
+7FF8
+6018
+6018
+6018
+0000
+0000
--- ./ter-u32b.bdf.orig 2020-12-28 20:34:28.225131800 +0200
+++ ./ter-u32b.bdf 2020-12-28 20:34:29.675133800 +0200
@@ -20554,8 +20554,6 @@
-3FC0
-7FE0
-70F0
-7070
-7070
-7070
-7070
-70E0
+0000
+0000
+0000
+0000
+0000
+0000
@@ -20563,0 +20562 @@
+7078
@@ -20565,4 +20564,5 @@
-701C
-701C
-701C
-701C
+7038
+7038
+7FF0
+7FF8
+703C
@@ -20638 +20638,12 @@
-1FFC
+07FC
+0FFC
+1E1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
+1C1C
@@ -20640,17 +20651,6 @@
-781C
-701C
-701C
-701C
-701C
-701C
-701C
-701C
-701C
-781C
-3FFC
-1FFC
-001C
-001C
-003C
-3FF8
-3FF0
+7FFE
+700E
+700E
+700E
+700E
+0000
--- ./ter-u32n.bdf.orig 2020-12-28 20:34:28.205131800 +0200
+++ ./ter-u32n.bdf 2020-12-28 20:34:29.675133800 +0200
@@ -20554,8 +20554,6 @@
-1FC0
-3FE0
-3070
-3030
-3030
-3030
-3030
-3070
+0000
+0000
+0000
+0000
+0000
+0000
@@ -20564,0 +20563,5 @@
+3018
+3018
+3038
+3FF0
+3FF8
@@ -20568,3 +20570,0 @@
-300C
-300C
-300C
@@ -20638,19 +20638,19 @@
-0FFC
-1FFC
-380C
-300C
-300C
-300C
-300C
-300C
-300C
-300C
-300C
-380C
-1FFC
-0FFC
-000C
-000C
-001C
-1FF8
-1FF0
+07F8
+0FF8
+1C18
+1818
+1818
+1818
+1818
+1818
+1818
+1818
+1818
+1818
+3FF8
+7FFC
+600C
+600C
+600C
+600C
+0000
+945
View File
@@ -0,0 +1,945 @@
--- ./ter-u12b.bdf.orig 2020-12-28 20:34:27.995131500 +0200
+++ ./ter-u12b.bdf 2020-12-28 20:34:30.355134800 +0200
@@ -10182,2 +10182,2 @@
-00
-00
+40
+40
@@ -10828,2 +10828,2 @@
-08
-10
+48
+50
@@ -11189,2 +11189,2 @@
-00
-00
+40
+40
@@ -11227,2 +11227,2 @@
-00
-00
+80
+80
@@ -11265,3 +11265,3 @@
-00
-00
-C8
+C0
+40
+48
--- ./ter-u12n.bdf.orig 2020-12-28 20:34:27.975131400 +0200
+++ ./ter-u12n.bdf 2020-12-28 20:34:30.315134700 +0200
@@ -10182,2 +10182,2 @@
-00
-00
+40
+40
@@ -10828,2 +10828,2 @@
-08
-10
+48
+50
@@ -11189,2 +11189,2 @@
-00
-00
+40
+40
@@ -11227,2 +11227,2 @@
-00
-00
+80
+80
@@ -11265,3 +11265,3 @@
-00
-00
-C8
+C0
+40
+48
--- ./ter-u14b.bdf.orig 2020-12-28 20:34:28.025131500 +0200
+++ ./ter-u14b.bdf 2020-12-28 20:34:30.365134800 +0200
@@ -11250,3 +11250,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -11964,3 +11964,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -12363,3 +12363,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12405,3 +12405,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12447,4 +12447,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u14n.bdf.orig 2020-12-28 20:34:27.995131500 +0200
+++ ./ter-u14n.bdf 2020-12-28 20:34:30.365134800 +0200
@@ -11250,3 +11250,3 @@
-00
-00
-00
+40
+40
+40
@@ -11964,3 +11964,3 @@
-08
-10
-00
+48
+50
+40
@@ -12363,3 +12363,3 @@
-00
-00
-00
+40
+40
+40
@@ -12405,3 +12405,3 @@
-00
-00
-00
+40
+40
+40
@@ -12447,4 +12447,4 @@
-00
-00
-00
-C2
+C0
+40
+40
+42
--- ./ter-u14v.bdf.orig 2020-12-28 20:34:28.025131500 +0200
+++ ./ter-u14v.bdf 2020-12-28 20:34:30.385134800 +0200
@@ -11250,3 +11250,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -11964,3 +11964,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -12363,3 +12363,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12405,3 +12405,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12447,4 +12447,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u16b.bdf.orig 2020-12-28 20:34:28.035131500 +0200
+++ ./ter-u16b.bdf 2020-12-28 20:34:30.405134800 +0200
@@ -12318,3 +12318,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13100,3 +13100,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -13537,3 +13537,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13583,3 +13583,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13629,4 +13629,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u16n.bdf.orig 2020-12-28 20:34:28.035131500 +0200
+++ ./ter-u16n.bdf 2020-12-28 20:34:30.395134800 +0200
@@ -12318,3 +12318,3 @@
-00
-00
-00
+40
+40
+40
@@ -13100,3 +13100,3 @@
-08
-10
-00
+48
+50
+40
@@ -13537,3 +13537,3 @@
-00
-00
-00
+40
+40
+40
@@ -13583,3 +13583,3 @@
-00
-00
-00
+40
+40
+40
@@ -13629,4 +13629,4 @@
-00
-00
-00
-C2
+C0
+40
+40
+42
--- ./ter-u16v.bdf.orig 2020-12-28 20:34:28.065131600 +0200
+++ ./ter-u16v.bdf 2020-12-28 20:34:30.405134800 +0200
@@ -12318,3 +12318,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13100,3 +13100,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -13537,3 +13537,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13583,3 +13583,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13629,4 +13629,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u18b.bdf.orig 2020-12-28 20:34:28.085131600 +0200
+++ ./ter-u18b.bdf 2020-12-28 20:34:30.445134900 +0200
@@ -13386,4 +13386,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -14237,3 +14237,3 @@
-0600
-0C00
-0000
+6600
+6C00
+6000
@@ -14712,3 +14712,3 @@
-0000
-0000
-0000
+6000
+6000
+6000
@@ -14762,3 +14762,3 @@
-0000
-0000
-0000
+6000
+6000
+6000
@@ -14811,5 +14811,5 @@
-0000
-0000
-0000
-0000
-E180
+8000
+E000
+6000
+6000
+6180
--- ./ter-u18n.bdf.orig 2020-12-28 20:34:28.065131600 +0200
+++ ./ter-u18n.bdf 2020-12-28 20:34:30.445134900 +0200
@@ -13386,4 +13386,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -14237,3 +14237,3 @@
-0400
-0800
-0000
+4400
+4800
+4000
@@ -14712,3 +14712,3 @@
-0000
-0000
-0000
+4000
+4000
+4000
@@ -14762,3 +14762,3 @@
-0000
-0000
-0000
+4000
+4000
+4000
@@ -14811,5 +14811,5 @@
-0000
-0000
-0000
-0000
-C100
+8000
+4000
+4000
+4000
+4100
--- ./ter-u20b.bdf.orig 2020-12-28 20:34:28.115131600 +0200
+++ ./ter-u20b.bdf 2020-12-28 20:34:30.455134900 +0200
@@ -14455,4 +14455,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -15373,4 +15373,4 @@
-0000
-0600
-0C00
-0000
+6000
+6600
+6C00
+6000
@@ -15886,4 +15886,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -15940,4 +15940,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -15994,5 +15994,5 @@
-0000
-0000
-0000
-0000
-E180
+E000
+E000
+6000
+6000
+6180
--- ./ter-u20n.bdf.orig 2020-12-28 20:34:28.085131600 +0200
+++ ./ter-u20n.bdf 2020-12-28 20:34:30.445134900 +0200
@@ -14455,4 +14455,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -15373,4 +15373,4 @@
-0000
-0400
-0800
-0000
+4000
+4400
+4800
+4000
@@ -15886,4 +15886,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -15940,4 +15940,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -15994,5 +15994,5 @@
-0000
-0000
-0000
-0000
-C100
+C000
+4000
+4000
+4000
+4100
--- ./ter-u22b.bdf.orig 2020-12-28 20:34:28.135131700 +0200
+++ ./ter-u22b.bdf 2020-12-28 20:34:30.505135000 +0200
@@ -15523,4 +15523,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -16509,4 +16509,4 @@
-0180
-0300
-0600
-0000
+3180
+3300
+3600
+3000
@@ -17060,4 +17060,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -17118,4 +17118,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -17176,6 +17176,6 @@
-0000
-0000
-0000
-0000
-70C0
-7180
+7000
+7000
+3000
+3000
+30C0
+3180
--- ./ter-u22n.bdf.orig 2020-12-28 20:34:28.115131600 +0200
+++ ./ter-u22n.bdf 2020-12-28 20:34:30.485134900 +0200
@@ -15523,4 +15523,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -16509,4 +16509,4 @@
-0100
-0200
-0400
-0000
+2100
+2200
+2400
+2000
@@ -17060,4 +17060,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -17118,4 +17118,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -17176,5 +17176,5 @@
-0000
-0000
-0000
-0000
-E080
+E000
+2000
+2000
+2000
+2080
--- ./ter-u24b.bdf.orig 2020-12-28 20:34:28.165131700 +0200
+++ ./ter-u24b.bdf 2020-12-28 20:34:30.515135000 +0200
@@ -16592,4 +16592,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -17646,4 +17646,4 @@
-0180
-0300
-0600
-0000
+3180
+3300
+3600
+3000
@@ -18235,4 +18235,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -18297,4 +18297,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -18359,6 +18359,6 @@
-0000
-0000
-0000
-0000
-7060
-70C0
+7000
+7000
+3000
+3000
+3060
+30C0
--- ./ter-u24n.bdf.orig 2020-12-28 20:34:28.135131700 +0200
+++ ./ter-u24n.bdf 2020-12-28 20:34:30.505135000 +0200
@@ -16592,4 +16592,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -17646,4 +17646,4 @@
-0100
-0200
-0400
-0000
+2100
+2200
+2400
+2000
@@ -18235,4 +18235,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -18297,4 +18297,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -18359,5 +18359,5 @@
-0000
-0000
-0000
-0000
-E040
+6000
+6000
+2000
+2000
+2040
--- ./ter-u28b.bdf.orig 2020-12-28 20:34:28.185131700 +0200
+++ ./ter-u28b.bdf 2020-12-28 20:34:30.555135000 +0200
@@ -18728,5 +18728,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -19918,5 +19918,5 @@
-0000
-01C0
-0380
-0700
-0000
+3000
+31C0
+3380
+3700
+3000
@@ -20583,5 +20583,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -20653,5 +20653,5 @@
-0000
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
+6000
@@ -20723,7 +20723,7 @@
-0000
-0000
-0000
-0000
-0000
-F070
-F0E0
+F000
+F000
+3000
+3000
+3000
+3070
+30E0
--- ./ter-u28n.bdf.orig 2020-12-28 20:34:28.175131700 +0200
+++ ./ter-u28n.bdf 2020-12-28 20:34:30.535135000 +0200
@@ -18728,5 +18728,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -19918,5 +19918,5 @@
-0000
-00C0
-0180
-0300
-0000
+3000
+30C0
+3180
+3300
+3000
@@ -20583,5 +20583,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -20653,5 +20653,5 @@
-0000
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
+6000
@@ -20723,7 +20723,7 @@
-0000
-0000
-0000
-0000
-0000
-F030
-F060
+F000
+F000
+3000
+3000
+3000
+3030
+3060
--- ./ter-u32b.bdf.orig 2020-12-28 20:34:28.225131800 +0200
+++ ./ter-u32b.bdf 2020-12-28 20:34:30.585135100 +0200
@@ -20866,6 +20866,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+3800
+3800
+3800
+3800
+3800
+3800
@@ -22192,6 +22192,6 @@
-0070
-00E0
-01C0
-0380
-0000
-0000
+3838
+3870
+38E0
+39C0
+3800
+3800
@@ -22933,6 +22933,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+3800
+3800
+3800
+3800
+3800
+3800
@@ -23011,6 +23011,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+7000
+7000
+7000
+7000
+7000
+7000
@@ -23089,8 +23089,8 @@
-0000
-0000
-0000
-0000
-0000
-0000
-F81C
-F838
+F800
+F800
+3800
+3800
+3800
+3800
+381C
+3838
--- ./ter-u32n.bdf.orig 2020-12-28 20:34:28.205131800 +0200
+++ ./ter-u32n.bdf 2020-12-28 20:34:30.585135100 +0200
@@ -20866,6 +20866,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+1800
+1800
+1800
+1800
+1800
+1800
@@ -22192,6 +22192,6 @@
-0070
-00E0
-01C0
-0380
-0000
-0000
+1838
+1870
+18E0
+19C0
+1800
+1800
@@ -22933,6 +22933,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+1800
+1800
+1800
+1800
+1800
+1800
@@ -23011,6 +23011,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
+3000
@@ -23089,8 +23089,8 @@
-0000
-0000
-0000
-0000
-0000
-0000
-781C
-7838
+7800
+7800
+1800
+1800
+1800
+1800
+181C
+1838
--- ./dup/xos4-2.dup.orig 2019-03-13 21:55:47.071547500 +0200
+++ ./dup/xos4-2.dup 2020-12-28 20:34:27.655131000 +0200
@@ -107 +107 @@
-0138 043A
+006B 043A
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+937
View File
@@ -0,0 +1,937 @@
--- ./ter-u12b.bdf.orig 2020-12-28 20:34:18.635118400 +0200
+++ ./ter-u12b.bdf 2020-12-28 20:34:27.095130200 +0200
@@ -10182,2 +10182,2 @@
-00
-00
+40
+40
@@ -10828,2 +10828,2 @@
-08
-10
+48
+50
@@ -11189,2 +11189,2 @@
-00
-00
+40
+40
@@ -11227,2 +11227,2 @@
-00
-00
+80
+80
@@ -11265,3 +11265,3 @@
-00
-00
-C8
+C0
+40
+48
--- ./ter-u12n.bdf.orig 2020-12-28 20:34:18.605118300 +0200
+++ ./ter-u12n.bdf 2020-12-28 20:34:27.065130200 +0200
@@ -10182,2 +10182,2 @@
-00
-00
+40
+40
@@ -10828,2 +10828,2 @@
-08
-10
+48
+50
@@ -11189,2 +11189,2 @@
-00
-00
+40
+40
@@ -11227,2 +11227,2 @@
-00
-00
+80
+80
@@ -11265,3 +11265,3 @@
-00
-00
-C8
+C0
+40
+48
--- ./ter-u14b.bdf.orig 2020-12-28 20:34:18.655118400 +0200
+++ ./ter-u14b.bdf 2020-12-28 20:34:27.105130200 +0200
@@ -11250,3 +11250,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -11964,3 +11964,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -12363,3 +12363,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12405,3 +12405,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12447,4 +12447,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u14n.bdf.orig 2020-12-28 20:34:18.645118400 +0200
+++ ./ter-u14n.bdf 2020-12-28 20:34:27.095130200 +0200
@@ -11250,3 +11250,3 @@
-00
-00
-00
+40
+40
+40
@@ -11964,3 +11964,3 @@
-08
-10
-00
+48
+50
+40
@@ -12363,3 +12363,3 @@
-00
-00
-00
+40
+40
+40
@@ -12405,3 +12405,3 @@
-00
-00
-00
+40
+40
+40
@@ -12447,4 +12447,4 @@
-00
-00
-00
-C2
+C0
+40
+40
+42
--- ./ter-u14v.bdf.orig 2020-12-28 20:34:18.675118400 +0200
+++ ./ter-u14v.bdf 2020-12-28 20:34:27.125130200 +0200
@@ -11250,3 +11250,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -11964,3 +11964,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -12363,3 +12363,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12405,3 +12405,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -12447,4 +12447,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u16b.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16b.bdf 2020-12-28 20:34:27.135130300 +0200
@@ -12318,3 +12318,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13100,3 +13100,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -13537,3 +13537,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13583,3 +13583,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13629,4 +13629,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u16n.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16n.bdf 2020-12-28 20:34:27.135130300 +0200
@@ -12318,3 +12318,3 @@
-00
-00
-00
+40
+40
+40
@@ -13100,3 +13100,3 @@
-08
-10
-00
+48
+50
+40
@@ -13537,3 +13537,3 @@
-00
-00
-00
+40
+40
+40
@@ -13583,3 +13583,3 @@
-00
-00
-00
+40
+40
+40
@@ -13629,4 +13629,4 @@
-00
-00
-00
-C2
+C0
+40
+40
+42
--- ./ter-u16v.bdf.orig 2020-12-28 20:34:18.695118400 +0200
+++ ./ter-u16v.bdf 2020-12-28 20:34:27.135130300 +0200
@@ -12318,3 +12318,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13100,3 +13100,3 @@
-18
-30
-00
+D8
+F0
+C0
@@ -13537,3 +13537,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13583,3 +13583,3 @@
-00
-00
-00
+C0
+C0
+C0
@@ -13629,4 +13629,4 @@
-00
-00
-00
-E3
+E0
+60
+60
+63
--- ./ter-u18b.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18b.bdf 2020-12-28 20:34:27.185130300 +0200
@@ -13387,3 +13387,3 @@
-0000
-0000
-0000
+6000
+6000
+6000
@@ -14237,3 +14237,3 @@
-0600
-0C00
-0000
+6600
+6C00
+6000
@@ -14712,3 +14712,3 @@
-0000
-0000
-0000
+6000
+6000
+6000
@@ -14762,3 +14762,3 @@
-0000
-0000
-0000
+6000
+6000
+6000
@@ -14812,4 +14812,4 @@
-0000
-0000
-0000
-E180
+E000
+E000
+6000
+6180
--- ./ter-u18n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18n.bdf 2020-12-28 20:34:27.175130300 +0200
@@ -13387,3 +13387,3 @@
-0000
-0000
-0000
+4000
+4000
+4000
@@ -14237,3 +14237,3 @@
-0400
-0800
-0000
+4400
+4800
+4000
@@ -14712,3 +14712,3 @@
-0000
-0000
-0000
+4000
+4000
+4000
@@ -14762,3 +14762,3 @@
-0000
-0000
-0000
+4000
+4000
+4000
@@ -14812,4 +14812,4 @@
-0000
-0000
-0000
-C100
+C000
+4000
+4000
+4100
--- ./ter-u20b.bdf.orig 2020-12-28 20:34:18.735118500 +0200
+++ ./ter-u20b.bdf 2020-12-28 20:34:27.195130300 +0200
@@ -14455,4 +14455,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -15373,4 +15373,4 @@
-0000
-0600
-0C00
-0000
+6000
+6600
+6C00
+6000
@@ -15886,4 +15886,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -15940,4 +15940,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -15994,5 +15994,5 @@
-0000
-0000
-0000
-0000
-E180
+E000
+E000
+6000
+6000
+6180
--- ./ter-u20n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u20n.bdf 2020-12-28 20:34:27.185130300 +0200
@@ -14455,4 +14455,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -15373,4 +15373,4 @@
-0000
-0400
-0800
-0000
+4000
+4400
+4800
+4000
@@ -15886,4 +15886,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -15940,4 +15940,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -15994,5 +15994,5 @@
-0000
-0000
-0000
-0000
-C100
+C000
+4000
+4000
+4000
+4100
--- ./ter-u22b.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22b.bdf 2020-12-28 20:34:27.235130400 +0200
@@ -15523,4 +15523,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -16509,4 +16509,4 @@
-0180
-0300
-0600
-0000
+3180
+3300
+3600
+3000
@@ -17060,4 +17060,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -17118,4 +17118,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -17176,6 +17176,6 @@
-0000
-0000
-0000
-0000
-70C0
-7180
+7000
+7000
+3000
+3000
+30C0
+3180
--- ./ter-u22n.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22n.bdf 2020-12-28 20:34:27.225130400 +0200
@@ -15523,4 +15523,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -16509,4 +16509,4 @@
-0100
-0200
-0400
-0000
+2100
+2200
+2400
+2000
@@ -17060,4 +17060,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -17118,4 +17118,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -17176,5 +17176,5 @@
-0000
-0000
-0000
-0000
-E080
+E000
+2000
+2000
+2000
+2080
--- ./ter-u24b.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24b.bdf 2020-12-28 20:34:27.245130400 +0200
@@ -16592,4 +16592,4 @@
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
@@ -17646,4 +17646,4 @@
-0180
-0300
-0600
-0000
+3180
+3300
+3600
+3000
@@ -18235,4 +18235,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -18297,4 +18297,4 @@
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
@@ -18359,6 +18359,6 @@
-0000
-0000
-0000
-0000
-7060
-70C0
+7000
+7000
+3000
+3000
+3060
+30C0
--- ./ter-u24n.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24n.bdf 2020-12-28 20:34:27.245130400 +0200
@@ -16592,4 +16592,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -17646,4 +17646,4 @@
-0100
-0200
-0400
-0000
+2100
+2200
+2400
+2000
@@ -18235,4 +18235,4 @@
-0000
-0000
-0000
-0000
+2000
+2000
+2000
+2000
@@ -18297,4 +18297,4 @@
-0000
-0000
-0000
-0000
+4000
+4000
+4000
+4000
@@ -18359,5 +18359,5 @@
-0000
-0000
-0000
-0000
-E040
+6000
+6000
+2000
+2000
+2040
--- ./ter-u28b.bdf.orig 2020-12-28 20:34:18.845118700 +0200
+++ ./ter-u28b.bdf 2020-12-28 20:34:27.295130500 +0200
@@ -18728,5 +18728,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -19918,5 +19918,5 @@
-0000
-01C0
-0380
-0700
-0000
+3000
+31C0
+3380
+3700
+3000
@@ -20583,5 +20583,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -20653,5 +20653,5 @@
-0000
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
+6000
@@ -20723,7 +20723,7 @@
-0000
-0000
-0000
-0000
-0000
-F070
-F0E0
+F000
+F000
+3000
+3000
+3000
+3070
+30E0
--- ./ter-u28n.bdf.orig 2020-12-28 20:34:18.835118600 +0200
+++ ./ter-u28n.bdf 2020-12-28 20:34:27.285130500 +0200
@@ -18728,5 +18728,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -19918,5 +19918,5 @@
-0000
-00C0
-0180
-0300
-0000
+3000
+30C0
+3180
+3300
+3000
@@ -20583,5 +20583,5 @@
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
@@ -20653,5 +20653,5 @@
-0000
-0000
-0000
-0000
-0000
+6000
+6000
+6000
+6000
+6000
@@ -20723,7 +20723,7 @@
-0000
-0000
-0000
-0000
-0000
-F030
-F060
+F000
+F000
+3000
+3000
+3000
+3030
+3060
--- ./ter-u32b.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32b.bdf 2020-12-28 20:34:27.305130500 +0200
@@ -20866,6 +20866,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+3800
+3800
+3800
+3800
+3800
+3800
@@ -22192,6 +22192,6 @@
-0070
-00E0
-01C0
-0380
-0000
-0000
+3838
+3870
+38E0
+39C0
+3800
+3800
@@ -22933,6 +22933,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+3800
+3800
+3800
+3800
+3800
+3800
@@ -23011,6 +23011,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+7000
+7000
+7000
+7000
+7000
+7000
@@ -23089,8 +23089,8 @@
-0000
-0000
-0000
-0000
-0000
-0000
-F81C
-F838
+F800
+F800
+3800
+3800
+3800
+3800
+381C
+3838
--- ./ter-u32n.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32n.bdf 2020-12-28 20:34:27.305130500 +0200
@@ -20866,6 +20866,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+1800
+1800
+1800
+1800
+1800
+1800
@@ -22192,6 +22192,6 @@
-0070
-00E0
-01C0
-0380
-0000
-0000
+1838
+1870
+18E0
+19C0
+1800
+1800
@@ -22933,6 +22933,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+1800
+1800
+1800
+1800
+1800
+1800
@@ -23011,6 +23011,6 @@
-0000
-0000
-0000
-0000
-0000
-0000
+3000
+3000
+3000
+3000
+3000
+3000
@@ -23089,8 +23089,8 @@
-0000
-0000
-0000
-0000
-0000
-0000
-781C
-7838
+7800
+7800
+1800
+1800
+1800
+1800
+181C
+1838
--- ./dup/xos4-2.dup.orig 2019-03-13 21:55:47.071547500 +0200
+++ ./dup/xos4-2.dup 2020-12-28 20:34:27.655131000 +0200
@@ -107 +107 @@
-0138 043A
+006B 043A
File diff suppressed because it is too large Load Diff
+236
View File
@@ -0,0 +1,236 @@
--- ./ter-u12b.bdf.orig 2020-12-28 20:34:18.635118400 +0200
+++ ./ter-u12b.bdf 2020-12-28 20:34:26.185128900 +0200
@@ -1840,3 +1839,0 @@
-48
-A8
-90
@@ -1846,0 +1844,3 @@
+48
+A8
+90
--- ./ter-u12n.bdf.orig 2020-12-28 20:34:18.605118300 +0200
+++ ./ter-u12n.bdf 2020-12-28 20:34:26.185128900 +0200
@@ -1840,3 +1839,0 @@
-48
-A8
-90
@@ -1846,0 +1844,3 @@
+48
+A8
+90
--- ./ter-u14b.bdf.orig 2020-12-28 20:34:18.655118400 +0200
+++ ./ter-u14b.bdf 2020-12-28 20:34:26.225129000 +0200
@@ -2030,3 +2029,0 @@
-73
-DB
-CE
@@ -2037,0 +2035,3 @@
+73
+DB
+CE
--- ./ter-u14n.bdf.orig 2020-12-28 20:34:18.645118400 +0200
+++ ./ter-u14n.bdf 2020-12-28 20:34:26.205129000 +0200
@@ -2030,3 +2029,0 @@
-62
-92
-8C
@@ -2037,0 +2035,3 @@
+62
+92
+8C
--- ./ter-u14v.bdf.orig 2020-12-28 20:34:18.675118400 +0200
+++ ./ter-u14v.bdf 2020-12-28 20:34:26.225129000 +0200
@@ -2030,3 +2029,0 @@
-73
-DB
-CE
@@ -2037,0 +2035,3 @@
+73
+DB
+CE
--- ./ter-u16b.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16b.bdf 2020-12-28 20:34:26.235129000 +0200
@@ -2220,3 +2219,0 @@
-73
-DB
-CE
@@ -2227,0 +2225,3 @@
+73
+DB
+CE
--- ./ter-u16n.bdf.orig 2020-12-28 20:34:18.685118400 +0200
+++ ./ter-u16n.bdf 2020-12-28 20:34:26.245129000 +0200
@@ -2220,3 +2219,0 @@
-62
-92
-8C
@@ -2227,0 +2225,3 @@
+62
+92
+8C
--- ./ter-u16v.bdf.orig 2020-12-28 20:34:18.695118400 +0200
+++ ./ter-u16v.bdf 2020-12-28 20:34:26.265129000 +0200
@@ -2220,3 +2219,0 @@
-73
-DB
-CE
@@ -2227,0 +2225,3 @@
+73
+DB
+CE
--- ./ter-u18b.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18b.bdf 2020-12-28 20:34:26.275129100 +0200
@@ -2410,4 +2409,0 @@
-3980
-6D80
-6D80
-6700
@@ -2419,0 +2416,4 @@
+3980
+6D80
+6D80
+6700
--- ./ter-u18n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u18n.bdf 2020-12-28 20:34:26.275129100 +0200
@@ -2410,4 +2409,0 @@
-3100
-4900
-4900
-4600
@@ -2419,0 +2416,4 @@
+3100
+4900
+4900
+4600
--- ./ter-u20b.bdf.orig 2020-12-28 20:34:18.735118500 +0200
+++ ./ter-u20b.bdf 2020-12-28 20:34:26.305129100 +0200
@@ -2600,4 +2599,0 @@
-3980
-6D80
-6D80
-6700
@@ -2610,0 +2607,4 @@
+3980
+6D80
+6D80
+6700
--- ./ter-u20n.bdf.orig 2020-12-28 20:34:18.725118500 +0200
+++ ./ter-u20n.bdf 2020-12-28 20:34:26.295129100 +0200
@@ -2600,4 +2599,0 @@
-3100
-4900
-4900
-4600
@@ -2610,0 +2607,4 @@
+3100
+4900
+4900
+4600
--- ./ter-u22b.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22b.bdf 2020-12-28 20:34:26.325129100 +0200
@@ -2790,4 +2789,0 @@
-38C0
-6CC0
-66C0
-6380
@@ -2800,0 +2797,4 @@
+38C0
+6CC0
+66C0
+6380
--- ./ter-u22n.bdf.orig 2020-12-28 20:34:18.775118600 +0200
+++ ./ter-u22n.bdf 2020-12-28 20:34:26.325129100 +0200
@@ -2790,4 +2789,0 @@
-3080
-4880
-4480
-4300
@@ -2800,0 +2797,4 @@
+3080
+4880
+4480
+4300
--- ./ter-u24b.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24b.bdf 2020-12-28 20:34:26.355129200 +0200
@@ -2981,4 +2980,0 @@
-3C60
-6660
-6660
-63C0
@@ -2992,0 +2989,4 @@
+3C60
+6660
+6660
+63C0
--- ./ter-u24n.bdf.orig 2020-12-28 20:34:18.795118600 +0200
+++ ./ter-u24n.bdf 2020-12-28 20:34:26.365129200 +0200
@@ -2981,4 +2980,0 @@
-3840
-4440
-4440
-4380
@@ -2992,0 +2989,4 @@
+3840
+4440
+4440
+4380
--- ./ter-u28b.bdf.orig 2020-12-28 20:34:18.845118700 +0200
+++ ./ter-u28b.bdf 2020-12-28 20:34:26.385129200 +0200
@@ -3361,5 +3360,0 @@
-3C30
-7E30
-6730
-63F0
-61E0
@@ -3374,0 +3370,5 @@
+3C30
+7E30
+6730
+63F0
+61E0
--- ./ter-u28n.bdf.orig 2020-12-28 20:34:18.835118600 +0200
+++ ./ter-u28n.bdf 2020-12-28 20:34:26.385129200 +0200
@@ -3361,5 +3360,0 @@
-3C30
-6630
-6330
-6330
-61E0
@@ -3374,0 +3370,5 @@
+3C30
+6630
+6330
+6330
+61E0
--- ./ter-u32b.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32b.bdf 2020-12-28 20:34:26.435129300 +0200
@@ -3743,6 +3742,0 @@
-1E1C
-3F1C
-779C
-73DC
-71F8
-70F0
@@ -3757,0 +3752,6 @@
+1E1C
+3F1C
+779C
+73DC
+71F8
+70F0
--- ./ter-u32n.bdf.orig 2020-12-28 20:34:18.865118700 +0200
+++ ./ter-u32n.bdf 2020-12-28 20:34:26.425129300 +0200
@@ -3743,6 +3742,0 @@
-0E0C
-1F0C
-3B8C
-31DC
-30F8
-3070
@@ -3757,0 +3752,6 @@
+0E0C
+1F0C
+3B8C
+31DC
+30F8
+3070
+135
View File
@@ -0,0 +1,135 @@
module.exports = {
'env': {
'es6': true,
'node': true,
'browser': false
},
'extends': 'eslint:recommended',
'parserOptions': {
'sourceType': 'module'
},
'rules': {
'indent': [
'error',
'tab'
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'warn',
'single'
],
'semi': [
'error',
'always'
],
'curly': [
'error',
'all'
],
'brace-style': [
'error',
'1tbs'
],
'no-empty' : 'warn',
'no-unused-vars' : 'warn',
'no-console': 'warn',
'consistent-return': 'error',
'class-methods-use-this': 'warn',
'eqeqeq': [
'error',
'always', {
'null': 'ignore'
}
],
'no-alert': 'warn',
'no-caller': 'error',
'no-eval': 'error',
'no-extend-native': 'warn',
'no-implicit-coercion': 'error',
'no-implied-eval': 'error',
'no-invalid-this': 'error',
'no-loop-func': 'error',
'no-new-func': 'warn',
'no-new-wrappers': 'error',
'no-proto': 'error',
'no-return-assign': 'warn',
'no-return-await': 'warn',
'no-script-url': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-throw-literal': 'error',
'no-unmodified-loop-condition': 'warn',
'no-unused-expressions': 'warn',
'no-useless-return': 'warn',
'no-warning-comments': 'warn',
'prefer-promise-reject-errors': 'warn',
'no-label-var': 'error',
'no-shadow': [
'warn', {
'builtinGlobals': true,
'hoist': 'all'
}
],
'no-shadow-restricted-names': 'error',
'no-undefined': 'error',
'no-use-before-define': 'error',
'no-new-require': 'error',
'no-path-concat': 'error',
'camelcase': 'error',
'comma-dangle': [
'error',
'never'
],
'eol-last': [
'error',
'always'
],
'func-call-spacing': 'warn',
'lines-around-directive': [
'warn',
'always'
],
'max-params': [
'warn', {
'max': 7
}
],
'max-statements-per-line': [
'warn', {
'max': 1
}
],
'new-cap': [
'error'
],
'no-array-constructor': 'warn',
'no-mixed-operators': [
'error', {
'groups': [
[ '&', '|', '^', '~', '<<', '>>', '>>>' ],
[ '==', '!=', '===', '!==', '>', '>=', '<', '<=' ],
[ '&&', '||' ],
[ 'in', 'instanceof' ]
],
'allowSamePrecedence': false
}
],
'no-trailing-spaces': 'warn',
'no-unneeded-ternary': 'warn',
'no-whitespace-before-property': 'error',
'operator-linebreak': 'warn',
'semi-spacing': 'warn',
'no-confusing-arrow': [
'error', {
'allowParens': true
}
],
'no-duplicate-imports': 'warn',
'prefer-rest-params': 'warn',
'prefer-spread': 'warn',
'no-unsafe-negation': 'warn'
}
};
+425
View File
@@ -0,0 +1,425 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint.
jobs=4
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,bad-continuation,missing-docstring,redefined-builtin,unnecessary-lambda,too-few-public-methods,too-many-locals,too-many-branches,too-many-statements,broad-except,consider-using-ternary
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Activate the evaluation score.
score=no
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=8
[BASIC]
# Naming hint for argument names
argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct argument names
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for attribute names
attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct attribute names
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class names
class-name-hint=[A-Z_][a-zA-Z0-9]+$
# Regular expression matching correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming hint for function names
function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct function names
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_,s,e,x,y,n
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for method names
method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct method names
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming hint for variable names
variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct variable names
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=LF
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string='\t'
# Maximum number of characters on a single line.
max-line-length=120
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,future.builtins
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=10
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of statements in function / method body
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[IMPORTS]
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
+330
View File
@@ -0,0 +1,330 @@
/*
Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
// -- Width --
const DPARSE_LIMIT = 512;
const SPARSE_LIMIT = 32000;
class Width {
constructor(x, y) {
this.x = x;
this.y = y;
}
static parse(name, value, limit) {
const words = fnutil.splitWords(name, value, 2);
return new Width(fnutil.parseDec(name + '.x', words[0], -limit, limit),
fnutil.parseDec(name + '.y', words[1], -limit, limit));
}
static parseS(name, value) {
return Width.parse(name, value, SPARSE_LIMIT);
}
static parseD(name, value) {
return Width.parse(name, value, DPARSE_LIMIT);
}
toString() {
return `${this.x} ${this.y}`;
}
}
// -- BBX --
class BBX {
constructor(width, height, xoff, yoff) {
this.width = width;
this.height = height;
this.xoff = xoff;
this.yoff = yoff;
}
static parse(name, value) {
const words = fnutil.splitWords(name, value, 4);
return new BBX(fnutil.parseDec(name + '.width', words[0], 1, DPARSE_LIMIT),
fnutil.parseDec(name + '.height', words[1], 1, DPARSE_LIMIT),
fnutil.parseDec(name + '.xoff', words[2], -DPARSE_LIMIT, DPARSE_LIMIT),
fnutil.parseDec(name + '.yoff', words[3], -DPARSE_LIMIT, DPARSE_LIMIT));
}
rowSize() {
return (this.width + 7) >> 3;
}
toString() {
return `${this.width} ${this.height} ${this.xoff} ${this.yoff}`;
}
}
// -- Props --
function skipComments(line) {
return line.startsWith('COMMENT') ? null : line;
}
class Props extends Map {
forEach(callback) {
super.forEach((value, name) => callback(name, value));
}
read(input, name, callback) {
return this.parse(input.readLines(skipComments), name, callback);
}
parse(line, name, callback) {
if (line == null || !line.startsWith(name)) {
throw new Error(name + ' expected');
}
const value = line.substring(name.length).trimLeft();
this.set(name, value);
return callback == null ? value : callback(name, value);
}
set(name, value) {
super.set(name, value.toString());
}
}
// -- Base --
class Base {
constructor() {
this.props = new Props();
this.bbx = null;
}
}
// -- Char --
class Char extends Base {
constructor() {
super();
this.code = -1;
this.swidth = null;
this.dwidth = null;
this.data = null;
}
bitmap() {
const bitmap = this.data.toString('hex').toUpperCase();
const regex = new RegExp(`.{${this.bbx.rowSize() << 1}}`, 'g');
return bitmap.replace(regex, '$&\n');
}
_read(input) {
// HEADER
this.props.read(input, 'STARTCHAR');
this.code = this.props.read(input, 'ENCODING', fnutil.parseDec);
this.swidth = this.props.read(input, 'SWIDTH', Width.parseS);
this.dwidth = this.props.read(input, 'DWIDTH', Width.parseD);
this.bbx = this.props.read(input, 'BBX', BBX.parse);
let line = input.readLines(skipComments);
if (line != null && line.startsWith('ATTRIBUTES')) {
this.props.parse(line, 'ATTRIBUTES');
line = input.readLines(skipComments);
}
// BITMAP
if (this.props.parse(line, 'BITMAP') !== '') {
throw new Error('BITMAP expected');
}
const rowLen = this.bbx.rowSize() * 2;
let bitmap = '';
for (let y = 0; y < this.bbx.height; y++) {
line = input.readLines(skipComments);
if (line == null) {
throw new Error('bitmap data expected');
}
if (line.match(/^[\dA-Fa-f]+$/) == null) {
throw new Error('invalid bitmap character(s)');
}
if (line.length === rowLen) {
bitmap += line;
} else {
throw new Error('invalid bitmap line length');
}
}
this.data = Buffer.from(bitmap, 'hex');
// FINAL
if (input.readLines(skipComments) !== 'ENDCHAR') {
throw new Error('ENDCHAR expected');
}
return this;
}
static read(input) {
return (new Char())._read(input);
}
write(output) {
let header = '';
this.props.forEach((name, value) => {
header += (name + ' ' + value).trim() + '\n';
});
output.writeLine(header + this.bitmap() + 'ENDCHAR');
}
}
// -- Font --
const XLFD = {
FOUNDRY: 1,
FAMILY_NAME: 2,
WEIGHT_NAME: 3,
SLANT: 4,
SETWIDTH_NAME: 5,
ADD_STYLE_NAME: 6,
PIXEL_SIZE: 7,
POINT_SIZE: 8,
RESOLUTION_X: 9,
RESOLUTION_Y: 10,
SPACING: 11,
AVERAGE_WIDTH: 12,
CHARSET_REGISTRY: 13,
CHARSET_ENCODING: 14
};
const CHARS_MAX = 65535;
class Font extends Base {
constructor() {
super();
this.chars = [];
this.defaultCode = -1;
}
get bold() {
return this.xlfd[XLFD.WEIGHT_NAME].toLowerCase().includes('bold');
}
get italic() {
return ['I', 'O'].indexOf(this.xlfd[XLFD.SLANT]) !== -1;
}
get proportional() {
return this.xlfd[XLFD.SPACING] === 'P';
}
_read(input) {
// HEADER
let line = input.readLine();
if (this.props.parse(line, 'STARTFONT') !== '2.1') {
throw new Error('STARTFONT 2.1 expected');
}
this.xlfd = this.props.read(input, 'FONT', (name, value) => value.split('-', 16));
if (this.xlfd.length !== 15 || this.xlfd[0] !== '') {
throw new Error('non-XLFD font names are not supported');
}
this.props.read(input, 'SIZE');
this.bbx = this.props.read(input, 'FONTBOUNDINGBOX', BBX.parse);
line = input.readLines(skipComments);
if (line != null && line.startsWith('STARTPROPERTIES')) {
const numProps = this.props.parse(line, 'STARTPROPERTIES', fnutil.parseDec);
for (let i = 0; i < numProps; i++) {
line = input.readLines(skipComments);
if (line == null) {
throw new Error('property expected');
}
const match = line.match(/^(\w+)\s+([-\d"].*)$/);
if (match == null) {
throw new Error('invalid property format');
}
const name = match[1];
const value = match[2];
if (this.props.get(name) != null) {
throw new Error('duplicate property');
}
if (name === 'DEFAULT_CHAR') {
this.defaultCode = fnutil.parseDec(name, value);
}
this.props.set(name, value);
}
if (this.props.read(input, 'ENDPROPERTIES') !== '') {
throw new Error('ENDPROPERTIES expected');
}
line = input.readLines(skipComments);
}
// GLYPHS
const numChars = fnutil.parseDec('CHARS', this.props.parse(line, 'CHARS'), 1, CHARS_MAX);
for (let i = 0; i < numChars; i++) {
this.chars.push(Char.read(input));
}
if (this.defaultCode !== -1 && this.chars.find(char => char.code === this.defaultCode) === -1) {
throw new Error('invalid DEFAULT_CHAR');
}
// FINAL
if (input.readLines(skipComments) !== 'ENDFONT') {
throw new Error('ENDFONT expected');
}
if (input.readLine() != null) {
throw new Error('garbage after ENDFONT');
}
return this;
}
static read(input) {
return (new Font())._read(input, false);
}
write(output) {
this.props.forEach((name, value) => output.writeProp(name, value));
this.chars.forEach(char => char.write(output));
output.writeLine('ENDFONT');
}
}
// -- Export --
module.exports = Object.freeze({
DPARSE_LIMIT,
SPARSE_LIMIT,
Width,
BBX,
skipComments,
Props,
Base,
Char,
XLFD,
CHARS_MAX,
Font
});
+309
View File
@@ -0,0 +1,309 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import re
import codecs
from collections import OrderedDict
from enum import IntEnum, unique
import fnutil
# -- Width --
DPARSE_LIMIT = 512
SPARSE_LIMIT = 32000
class Width:
def __init__(self, x, y):
self.x = x
self.y = y
@staticmethod
def parse(name, value, limit):
words = fnutil.split_words(name, value, 2)
return Width(fnutil.parse_dec(name + '.x', words[0], -limit, limit),
fnutil.parse_dec(name + '.y', words[1], -limit, limit))
@staticmethod
def parse_s(name, value):
return Width.parse(name, value, SPARSE_LIMIT)
@staticmethod
def parse_d(name, value):
return Width.parse(name, value, DPARSE_LIMIT)
def __str__(self):
return '%d %d' % (self.x, self.y)
# -- BXX --
class BBX:
def __init__(self, width, height, xoff, yoff):
self.width = width
self.height = height
self.xoff = xoff
self.yoff = yoff
@staticmethod
def parse(name, value):
words = fnutil.split_words(name, value, 4)
return BBX(fnutil.parse_dec('width', words[0], 1, DPARSE_LIMIT),
fnutil.parse_dec('height', words[1], 1, DPARSE_LIMIT),
fnutil.parse_dec('bbxoff', words[2], -DPARSE_LIMIT, DPARSE_LIMIT),
fnutil.parse_dec('bbyoff', words[3], -DPARSE_LIMIT, DPARSE_LIMIT))
def row_size(self):
return (self.width + 7) >> 3
def __str__(self):
return '%d %d %d %d' % (self.width, self.height, self.xoff, self.yoff)
# -- Props --
def skip_comments(line):
return None if line[:7] == b'COMMENT' else line
class Props(OrderedDict):
def __iter__(self):
return self.items().__iter__()
def read(self, input, name, callback=None):
return self.parse(input.read_lines(skip_comments), name, callback)
def parse(self, line, name, callback=None):
if not line or not line.startswith(bytes(name, 'ascii')):
raise Exception(name + ' expected')
value = line[len(name):].lstrip()
self[name] = value
return value if callback is None else callback(name, value)
def set(self, name, value):
self[name] = value if isinstance(value, (bytes, bytearray)) else bytes(str(value), 'ascii')
# -- Base --
class Base:
def __init__(self):
self.props = Props()
self.bbx = None
# -- Char
HEX_BYTES = (48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70)
class Char(Base):
def __init__(self):
Base.__init__(self)
self.code = -1
self.swidth = None
self.dwidth = None
self.data = None
def bitmap(self):
bitmap = ''
row_size = self.bbx.row_size()
for index in range(0, len(self.data), row_size):
bitmap += self.data[index : index + row_size].hex() + '\n'
return bytes(bitmap, 'ascii').upper()
def _read(self, input):
# HEADER
self.props.read(input, 'STARTCHAR')
self.code = self.props.read(input, 'ENCODING', fnutil.parse_dec)
self.swidth = self.props.read(input, 'SWIDTH', Width.parse_s)
self.dwidth = self.props.read(input, 'DWIDTH', Width.parse_d)
self.bbx = self.props.read(input, 'BBX', BBX.parse)
line = input.read_lines(skip_comments)
if line and line.startswith(b'ATTRIBUTES'):
self.props.parse(line, 'ATTRIBUTES')
line = input.read_lines(skip_comments)
# BITMAP
if self.props.parse(line, 'BITMAP'):
raise Exception('BITMAP expected')
row_len = self.bbx.row_size() * 2
self.data = bytearray()
for _ in range(0, self.bbx.height):
line = input.read_lines(skip_comments)
if not line:
raise Exception('bitmap data expected')
if len(line) == row_len:
self.data += codecs.decode(line, 'hex')
else:
raise Exception('invalid bitmap length')
# FINAL
if input.read_lines(skip_comments) != b'ENDCHAR':
raise Exception('ENDCHAR expected')
return self
@staticmethod
def read(input):
return Char()._read(input) # pylint: disable=protected-access
def write(self, output):
for [name, value] in self.props:
output.write_prop(name, value)
output.write_line(self.bitmap() + b'ENDCHAR')
# -- Font --
@unique
class XLFD(IntEnum):
FOUNDRY = 1
FAMILY_NAME = 2
WEIGHT_NAME = 3
SLANT = 4
SETWIDTH_NAME = 5
ADD_STYLE_NAME = 6
PIXEL_SIZE = 7
POINT_SIZE = 8
RESOLUTION_X = 9
RESOLUTION_Y = 10
SPACING = 11
AVERAGE_WIDTH = 12
CHARSET_REGISTRY = 13
CHARSET_ENCODING = 14
CHARS_MAX = 65535
class Font(Base):
def __init__(self):
Base.__init__(self)
self.xlfd = []
self.chars = []
self.default_code = -1
@property
def bold(self):
return b'bold' in self.xlfd[XLFD.WEIGHT_NAME].lower()
@property
def italic(self):
return self.xlfd[XLFD.SLANT] in [b'I', b'O']
@property
def proportional(self):
return self.xlfd[XLFD.SPACING] == b'P'
def _read(self, input):
# HEADER
line = input.read_line()
if self.props.parse(line, 'STARTFONT') != b'2.1':
raise Exception('STARTFONT 2.1 expected')
self.xlfd = self.props.read(input, 'FONT', lambda name, value: value.split(b'-', 15))
if len(self.xlfd) != 15 or self.xlfd[0] != b'':
raise Exception('non-XLFD font names are not supported')
self.props.read(input, 'SIZE')
self.bbx = self.props.read(input, 'FONTBOUNDINGBOX', BBX.parse)
line = input.read_lines(skip_comments)
if line and line.startswith(b'STARTPROPERTIES'):
num_props = self.props.parse(line, 'STARTPROPERTIES', fnutil.parse_dec)
for _ in range(0, num_props):
line = input.read_lines(skip_comments)
if line is None:
raise Exception('property expected')
match = re.fullmatch(br'(\w+)\s+([-\d"].*)', line)
if not match:
raise Exception('invalid property format')
name = str(match.group(1), 'ascii')
value = match.group(2)
if self.props.get(name) is not None:
raise Exception('duplicate property')
if name == 'DEFAULT_CHAR':
self.default_code = fnutil.parse_dec(name, value)
self.props[name] = value
if self.props.read(input, 'ENDPROPERTIES') != b'':
raise Exception('ENDPROPERTIES expected')
line = input.read_lines(skip_comments)
# GLYPHS
num_chars = fnutil.parse_dec('CHARS', self.props.parse(line, 'CHARS'), 1, CHARS_MAX)
for _ in range(0, num_chars):
self.chars.append(Char.read(input))
if next((char.code for char in self.chars if char.code == self.default_code), -1) != self.default_code:
raise Exception('invalid DEFAULT_CHAR')
# FINAL
if input.read_lines(skip_comments) != b'ENDFONT':
raise Exception('ENDFONT expected')
if input.read_line() is not None:
raise Exception('garbage after ENDFONT')
return self
@staticmethod
def read(input):
return Font()._read(input) # pylint: disable=protected-access
def write(self, output):
for [name, value] in self.props:
output.write_prop(name, value)
for char in self.chars:
char.write(output)
output.write_line(b'ENDFONT')
+455
View File
@@ -0,0 +1,455 @@
/*
Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
const fncli = require('./fncli.js');
const fnio = require('./fnio.js');
const bdf = require('./bdf.js');
// -- Params --
class Params extends fncli.Params {
constructor() {
super();
this.asciiChars = true;
this.bbxExceeds = true;
this.duplCodes = -1;
this.extraBits = true;
this.attributes = true;
this.duplNames = -1;
this.duplProps = true;
this.commonSlant = true;
this.commonWeight = true;
this.xlfdFontNm = true;
this.yWidthZero = true;
}
}
// -- Options --
const HELP = ('' +
'usage: bdfcheck [options] [INPUT...]\n' +
'Check BDF font(s) for various problems\n' +
'\n' +
' -A disable non-ascii characters check\n' +
' -B disable BBX exceeding FONTBOUNDINGBOX checks\n' +
' -c/-C enable/disable duplicate character codes check\n' +
' (default = enabled for registry ISO10646)\n' +
' -E disable extra bits check\n' +
' -I disable ATTRIBUTES check\n' +
' -n/-N enable duplicate character names check\n' +
' (default = enabled for registry ISO10646)\n' +
' -P disable duplicate properties check\n' +
' -S disable common slant check\n' +
' -W disable common weight check\n' +
' -X disable XLFD font name check\n' +
' -Y disable zero WIDTH Y check\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'File directives: COMMENT bdfcheck --enable|disable-<check-name>\n' +
' (also available as long command line options)\n' +
'\n' +
'Check names: ascii-chars, bbx-exceeds, duplicate-codes, extra-bits,\n' +
' attributes, duplicate-names, duplicate-properties, common-slant,\n' +
' common-weight, xlfd-font, ywidth-zero\n' +
'\n' +
'The input BDF(s) must be v2.1 with unicode encoding.\n');
const VERSION = 'bdfcheck 1.61, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE;
class Options extends fncli.Options {
constructor() {
super([], HELP, VERSION);
}
parse(name, directive, params) {
const value = name.startsWith('--enable') || name[1].match('[a-z]');
switch (name) {
case '-A':
case '--enable-ascii-chars':
case '--disable-ascii-chars':
params.asciiChars = value;
break;
case '-B':
case '--enable-bbx-exceeds':
case '--disable-bbx-exceeds':
params.bbxExceeds = value;
break;
case '-c':
case '-C':
case '--enable-duplicate-codes':
case '--disable-duplicate-codes':
params.duplCodes = value;
break;
case '-E':
case '--enable-extra-bits':
case '--disable-extra-bits':
params.extraBits = value;
break;
case '-I':
case '--enable-attributes':
case '--disable-attributes':
params.attributes = value;
break;
case '-n':
case '-N':
case '--enable-duplicate-names':
case '--disable-duplicate-names':
params.duplNames = value;
break;
case '-P':
case '--enable-duplicate-properties':
case '--disable-duplicate-properties':
params.duplProps = value;
break;
case '-S':
case '--enable-common-slant':
case '--disable-common-slant':
params.commonSlant = value;
break;
case '-W':
case '--enable-common-weight':
case '--disable-common-weight':
params.commonWeight = value;
break;
case '-X':
case '--enable-xlfd-font':
case '--disable-xlfd-font':
params.xlfdFontNm = value;
break;
case '-Y':
case '--enable-ywidth-zero':
case '--disable-ywidth-zero':
params.yWidthZero = value;
break;
default:
return directive !== true && this.fallback(name, params);
}
return directive !== true || name.startsWith('--');
}
}
// -- DupMap --
class DupMap extends Map {
constructor(prefix, descript, severity) {
super();
this.prefix = prefix;
this.descript = descript;
this.severity = severity;
}
check() {
this.forEach((lines, value) => {
if (lines.length > 1) {
let text = `duplicate ${this.descript} ${value} at lines`;
for (let index = 0; index < lines.length; index++) {
text += (index === 0 ? ' ' : index === lines.length - 1 ? ' and ' : ', ');
text += lines[index];
}
fnutil.message(this.prefix, this.severity, text);
}
});
}
push(value, lineNo) {
let lines = this.get(value);
if (lines != null) {
lines.push(lineNo);
} else {
this.set(value, [lineNo]);
}
}
}
// -- InputFileStream --
const MODE = Object.freeze({
META: 0,
PROPS: 1,
BITMAP: 2
});
class InputFileStream extends fnio.InputFileStream {
constructor(fileName, parsed) {
super(fileName);
this.parsed = parsed;
this.mode = MODE.META;
this.proplocs = new DupMap(this.location(), 'property');
this.namelocs = new DupMap(this.location(), 'character name', 'warning');
this.codelocs = new DupMap(this.location(), 'encoding', 'warning');
this.HANDLERS = [
[ 'STARTCHAR', value => this.appendName(value) ],
[ 'ENCODING', value => this.appendCode(value) ],
[ 'SWIDTH', value => this.checkWidth('SWIDTH', value, bdf.Width.parseS) ],
[ 'DWIDTH', value => this.checkWidth('DWIDTH', value, bdf.Width.parseD) ],
[ 'BBX', value => this.setLastBox(value) ],
[ 'BITMAP', () => this.setMode(MODE.BITMAP) ],
[ 'SIZE', InputFileStream.checkSize ],
[ 'ATTRIBUTES', value => this.checkAttr(value) ],
[ 'STARTPROPERTIES', () => this.setMode(MODE.PROPS) ],
[ 'FONTBOUNDINGBOX', value => this.setFontBox(value) ]
];
this.xlfdName = false;
this.lastBox = null;
this.fontBox = null;
this.options = new Options();
}
append(option, valocs, value) {
if (option) {
valocs.push(value, this.lineNo);
}
}
appendCode(value) {
fnutil.parseDec('encoding', value);
this.append(this.parsed.duplCodes, this.codelocs, value);
}
appendName(value) {
this.append(this.parsed.duplNames, this.namelocs, `"${value}"`);
}
checkWidth(name, value, parse) {
if (this.parsed.yWidthZero && parse(name, value).y !== 0) {
fnutil.warning(this.location(), `non-zero ${name} Y`);
}
}
setFontBox(value) {
this.fontBox = bdf.BBX.parse('FONTBOUNDINGBOX', value);
}
setLastBox(value) {
const bbx = bdf.BBX.parse('BBX', value);
if (this.parsed.bbxExceeds) {
let exceeds = [];
if (bbx.xoff < this.fontBox.xoff) {
exceeds.push('xoff < FONTBOUNDINGBOX xoff');
}
if (bbx.yoff < this.fontBox.yoff) {
exceeds.push('yoff < FONTBOUNDINGBOX yoff');
}
if (bbx.width > this.fontBox.width) {
exceeds.push('width > FONTBOUNDINGBOX width');
}
if (bbx.height > this.fontBox.height) {
exceeds.push('height > FONTBOUNDINGBOX height');
}
exceeds.forEach(exceed => {
fnutil.message(this.location(), '', exceed);
});
}
this.lastBox = bbx;
}
setMode(newMode) {
this.mode = newMode;
}
static checkSize(value) {
const words = fnutil.splitWords('SIZE', value, 3);
fnutil.parseDec('point size', words[0], 1, null);
fnutil.parseDec('x resolution', words[1], 1, null);
fnutil.parseDec('y resolution', words[2], 1, null);
}
checkAttr(value) {
if (!value.match(/^[\dA-Fa-f]{4}$/)) {
throw new Error('ATTRIBUTES must be 4 hex-encoded characters');
}
if (this.parsed.attributes) {
fnutil.warning(this.location(), 'ATTRIBUTES may cause problems with freetype');
}
}
checkFont(value) {
const xlfd = value.substring(4).trimLeft().split('-', 16);
if (xlfd.length === 15 && xlfd[0] === '') {
let unicode = (xlfd[bdf.XLFD.CHARSET_REGISTRY].toUpperCase() === 'ISO10646');
if (this.parsed.duplCodes === -1) {
this.parsed.duplCodes = unicode;
}
if (this.parsed.duplNames === -1) {
this.parsed.duplNames = unicode;
}
if (this.parsed.commonWeight) {
let weight = xlfd[bdf.XLFD.WEIGHT_NAME];
let compare = weight.toLowerCase();
let consider = compare.includes('bold') ? 'Bold' : 'Normal';
if (compare === 'medium' || compare === 'regular') {
compare = 'normal';
}
if (compare !== consider.toLowerCase()) {
fnutil.warning(this.location(), `weight "${weight}" may be considered ${consider}`);
}
}
if (this.parsed.commonSlant) {
let slant = xlfd[bdf.XLFD.SLANT];
let consider = slant.match(/^[IO]/) ? 'Italic' : 'Regular';
if (slant.match(/^[IOR]$/) == null) {
fnutil.warning(this.location(), `slant "${slant}" may be considered ${consider}`);
}
}
} else {
if (this.parsed.xlfdFontNm) {
fnutil.warning(this.location(), 'non-XLFD font name');
}
value = 'FONT --------------';
}
return value;
}
checkProp(line) {
const match = line.match(/^(\w+)\s+([-\d"].*)$/);
if (match == null) {
throw new Error('invalid property format');
}
const name = match[1];
const value = match[2];
if (value.startsWith('"')) {
if (value.length < 2 || !value.endsWith('"')) {
throw new Error('no closing double quote');
}
if (value.substring(1, value.length - 1).match(/[^"]"[^"]/)) {
throw new Error('unescaped double quote');
}
} else {
fnutil.parseDec('value', value, null, null);
}
this.append(this.parsed.duplProps, this.proplocs, name);
return `P${this.lineNo} 1`;
}
checkBitmap(line) {
if (line.length !== this.lastBox.rowSize() * 2) {
throw new Error('invalid bitmap length');
} else if (line.match(/^[\dA-Fa-f]+$/) == null) {
throw new Error('invalid bitmap data');
} else if (this.parsed.extraBits) {
const data = Buffer.from(line, 'hex');
const checkX = (this.lastBox.width - 1) | 7;
const lastByte = data[data.length - 1];
let bitNo = 7 - (this.lastBox.Width & 7);
for (let x = this.lastBox.Width; x <= checkX; x++) {
if (lastByte & (1 << bitNo)) {
fnutil.warning(this.location(), `extra bit(s) starting with x=${x}`);
break;
}
bitNo--;
}
}
}
checkLine(line) {
if (line.match(/[^\t\f\v\u0020-\u00ff]/)) {
throw new Error('control character(s)');
}
if (this.parsed.asciiChars && line.match(/[\u007f-\u00ff]/)) {
fnutil.warning(this.location(), 'non-ascii character(s)');
}
switch (this.mode) {
case MODE.META:
if (!this.xlfdName && line.startsWith('FONT')) {
line = this.checkFont(line);
this.xlfdName = true;
} else {
this.HANDLERS.findIndex(function(handler) {
if (line.startsWith(handler[0])) {
handler[1](line.substring(handler[0].length).trimLeft());
return true;
}
return false;
});
}
break;
case MODE.PROPS:
if (line.startsWith('ENDPROPERTIES')) {
this.mode = MODE.META;
} else {
line = this.checkProp(line);
}
break;
default: // MODE.BITMAP
if (line.startsWith('ENDCHAR')) {
this.mode = MODE.META;
} else {
this.checkBitmap(line);
}
}
return line;
}
readCheck(line, callback) {
const match = line.match(/^COMMENT\s*bdfcheck\s+(-.*)$/);
if (match && !this.options.parse(match[1], true, this.parsed)) {
throw new Error('invalid bdfcheck directive');
}
line = callback(line);
return line != null ? this.checkLine(line) : null;
}
readLines(callback) {
return super.readLines(line => this.readCheck(line, callback));
}
}
// -- Main --
function mainProgram(nonopt, parsed) {
(nonopt.length >= 1 ? nonopt : [null]).forEach(input => {
let ifs = new InputFileStream(input, parsed);
try {
bdf.Font.read(ifs);
ifs.close();
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
ifs.proplocs.check();
ifs.namelocs.check();
ifs.codelocs.check();
});
}
if (require.main === module) {
fncli.start('bdfcheck.js', new Options(), new Params(), mainProgram);
}
+380
View File
@@ -0,0 +1,380 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import re
import codecs
from collections import OrderedDict
from enum import IntEnum, unique
import fnutil
import fncli
import fnio
import bdf
# -- Params --
class Params(fncli.Params): # pylint: disable=too-many-instance-attributes
def __init__(self):
fncli.Params.__init__(self)
self.ascii_chars = True
self.bbx_exceeds = True
self.dupl_codes = -1
self.extra_bits = True
self.attributes = True
self.dupl_names = -1
self.dupl_props = True
self.common_slant = True
self.common_weight = True
self.xlfd_fontnm = True
self.ywidth_zero = True
# -- Options --
HELP = ('' +
'usage: bdfcheck [options] [INPUT...]\n' +
'Check BDF font(s) for various problems\n' +
'\n' +
' -A disable non-ascii characters check\n' +
' -B disable BBX exceeding FONTBOUNDINGBOX checks\n' +
' -c/-C enable/disable duplicate character codes check\n' +
' (default = enabled for registry ISO10646)\n' +
' -E disable extra bits check\n' +
' -I disable ATTRIBUTES check\n' +
' -n/-N enable duplicate character names check\n' +
' (default = enabled for registry ISO10646)\n' +
' -P disable duplicate properties check\n' +
' -S disable common slant check\n' +
' -W disable common weight check\n' +
' -X disable XLFD font name check\n' +
' -Y disable zero WIDTH Y check\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'File directives: COMMENT bdfcheck --enable|disable-<check-name>\n' +
' (also available as long command line options)\n' +
'\n' +
'Check names: ascii-chars, bbx-exceeds, duplicate-codes, extra-bits,\n' +
' attributes, duplicate-names, duplicate-properties, common-slant,\n' +
' common-weight, xlfd-font, ywidth-zero\n' +
'\n' +
'The input BDF(s) must be v2.1 with unicode encoding.\n')
VERSION = 'bdfcheck 1.62, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE
class Options(fncli.Options):
def __init__(self):
fncli.Options.__init__(self, [], HELP, VERSION)
def parse(self, name, directive, params):
value = name.startswith('--enable') or name[1].islower()
if name in ['-A', '--enable-ascii-chars', '--disable-ascii-chars']:
params.ascii_chars = value
elif name in ['-B', '--enable-bbx-exceeds', '--disable-bbx-exceeds']:
params.bbx_exceeds = value
elif name in ['-c', '-C', '--enable-duplicate-codes', '--disable-duplicate-codes']:
params.dupl_codes = value
elif name in ['-E', '--enable-extra-bits', '--disable-extra-bits']:
params.extra_bits = value
elif name in ['-I', '--enable-attributes', '--disable-attributes']:
params.attributes = value
elif name in ['-n', '-N', '--enable-duplicate-names', '--disable-duplicate-names']:
params.dupl_names = value
elif name in ['-P', '--enable-duplicate-properties', '--disable-duplicate-properties']:
params.dupl_props = value
elif name in ['-S', '--enable-common-slant', '--disable-common-slant']:
params.common_slant = value
elif name in ['-W', '--enable-common-weight', '--disable-common-weight']:
params.common_weight = value
elif name in ['-X', '--enable-xlfd-font', '--disable-xlfd-font']:
params.xlfd_fontnm = value
elif name in ['-Y', '--enable-ywidth-zero', '--disable-ywidth-zero']:
params.ywidth_zero = value
else:
return directive is not True and self.fallback(name, params)
return directive is not True or name.startswith('--')
# -- DupMap --
class DupMap(OrderedDict):
def __init__(self, prefix, severity, descript, quote):
OrderedDict.__init__(self)
self.prefix = prefix
self.descript = descript
self.severity = severity
self.quote = quote
def check(self):
for value, lines in self.items():
if len(lines) > 1:
text = 'duplicate %s %s at lines' % (self.descript, str(value))
for index, line in enumerate(lines):
text += ' ' if index == 0 else ' and ' if index == len(lines) - 1 else ', '
text += str(line)
fnutil.message(self.prefix, self.severity, text)
def push(self, value, line_no):
try:
self[value].append(line_no)
except KeyError:
self[value] = [line_no]
# -- InputFileStream --
@unique
class MODE(IntEnum):
META = 0
PROPS = 1
BITMAP = 2
class InputFileStream(fnio.InputFileStream):
def __init__(self, file_name, parsed):
fnio.InputFileStream.__init__(self, file_name)
self.parsed = parsed
self.mode = MODE.META
self.proplocs = DupMap(self.location(), 'error', 'property', '')
self.namelocs = DupMap(self.location(), 'warning', 'character name', '"')
self.codelocs = DupMap(self.location(), 'warning', 'encoding', '')
self.handlers = [
(b'STARTCHAR', lambda value: self.append_name(value)),
(b'ENCODING', lambda value: self.append_code(value)),
(b'SWIDTH', lambda value: self.check_width('SWIDTH', value, bdf.Width.parse_s)),
(b'DWIDTH', lambda value: self.check_width('DWIDTH', value, bdf.Width.parse_d)),
(b'BBX', lambda value: self.set_last_box(value)),
(b'BITMAP', lambda _: self.set_mode(MODE.BITMAP)),
(b'SIZE', InputFileStream.check_size),
(b'ATTRIBUTES', lambda value: self.check_attr(value)),
(b'STARTPROPERTIES', lambda _: self.set_mode(MODE.PROPS)),
(b'FONTBOUNDINGBOX', lambda value: self.set_font_box(value)),
]
self.xlfd_name = False
self.last_box = None
self.font_box = None
self.options = Options()
def append(self, option, valocs, value):
if option:
valocs.push(str(value, 'ascii'), self.line_no)
def append_code(self, value):
fnutil.parse_dec('encoding', value)
self.append(self.parsed.dupl_codes, self.codelocs, value)
def append_name(self, value):
self.append(self.parsed.dupl_names, self.namelocs, b'"%s"' % value)
def check_width(self, name, value, parse):
if self.parsed.ywidth_zero and parse(name, value).y != 0:
fnutil.warning(self.location(), 'non-zero %s Y' % name)
def set_font_box(self, value):
self.font_box = bdf.BBX.parse('FONTBOUNDINGBOX', value)
def set_last_box(self, value):
bbx = bdf.BBX.parse('BBX', value)
if self.parsed.bbx_exceeds:
exceeds = []
if bbx.xoff < self.font_box.xoff:
exceeds.append('xoff < FONTBOUNDINGBOX xoff')
if bbx.yoff < self.font_box.yoff:
exceeds.append('yoff < FONTBOUNDINGBOX yoff')
if bbx.width > self.font_box.width:
exceeds.append('width > FONTBOUNDINGBOX width')
if bbx.height > self.font_box.height:
exceeds.append('height > FONTBOUNDINGBOX height')
for exceed in exceeds:
fnutil.message(self.location(), '', exceed)
self.last_box = bbx
def set_mode(self, new_mode):
self.mode = new_mode
def check(self):
self.process(bdf.Font.read)
self.proplocs.check()
self.namelocs.check()
self.codelocs.check()
@staticmethod
def check_size(value):
words = fnutil.split_words('SIZE', value, 3)
fnutil.parse_dec('point size', words[0], 1, None)
fnutil.parse_dec('x resolution', words[1], 1, None)
fnutil.parse_dec('y resolution', words[2], 1, None)
def check_attr(self, value):
if not re.fullmatch(br'[\dA-Fa-f]{4}', value):
raise Exception('ATTRIBUTES must be 4 hex-encoded characters')
if self.parsed.attributes:
fnutil.warning(self.location(), 'ATTRIBUTES may cause problems with freetype')
def check_font(self, value):
xlfd = value[4:].lstrip().split(b'-', 15)
if len(xlfd) == 15 and xlfd[0] == b'':
unicode = (xlfd[bdf.XLFD.CHARSET_REGISTRY].upper() == b'ISO10646')
if self.parsed.dupl_codes == -1:
self.parsed.dupl_codes = unicode
if self.parsed.dupl_names == -1:
self.parsed.dupl_names = unicode
if self.parsed.common_weight:
weight = str(xlfd[bdf.XLFD.WEIGHT_NAME], 'ascii')
compare = weight.lower()
consider = 'Bold' if 'bold' in compare else 'Normal'
if compare in ['medium', 'regular']:
compare = 'normal'
if compare != consider.lower():
fnutil.warning(self.location(), 'weight "%s" may be considered %s' % (weight, consider))
if self.parsed.common_slant:
slant = str(xlfd[bdf.XLFD.SLANT], 'ascii')
consider = 'Italic' if re.search('^[IO]', slant) else 'Regular'
if not re.fullmatch('[IOR]', slant):
fnutil.warning(self.location(), 'slant "%s" may be considered %s' % (slant, consider))
else:
if self.parsed.xlfd_fontnm:
fnutil.warning(self.location(), 'non-XLFD font name')
value = b'FONT --------------'
return value
def check_prop(self, line):
match = re.fullmatch(br'(\w+)\s+([-\d"].*)', line)
if not match:
raise Exception('invalid property format')
name = match.group(1)
value = match.group(2)
if value.startswith(b'"'):
if len(value) < 2 or not value.endswith(b'"'):
raise Exception('no closing double quote')
if re.search(b'[^"]"[^"]', value[1 : len(value) - 1]):
raise Exception('unescaped double quote')
else:
fnutil.parse_dec('value', value, None, None)
self.append(self.parsed.dupl_props, self.proplocs, name)
return b'P%d 1' % self.line_no
def check_bitmap(self, line):
if len(line) != self.last_box.row_size() * 2:
raise Exception('invalid bitmap length')
data = codecs.decode(line, 'hex')
if self.parsed.extra_bits:
check_x = (self.last_box.width - 1) | 7
last_byte = data[len(data) - 1]
bit_no = 7 - (self.last_box.width & 7)
for x in range(self.last_box.width, check_x + 1):
if last_byte & (1 << bit_no):
fnutil.warning(self.location(), 'extra bit(s) starting with x=%d' % x)
break
bit_no -= 1
def check_line(self, line):
if re.search(b'[^\t\f\v\x20-\xff]', line):
raise Exception('control character(s)')
if self.parsed.ascii_chars and re.search(b'[\x7f-\xff]', line):
fnutil.warning(self.location(), 'non-ascii character(s)')
if self.mode == MODE.META:
if not self.xlfd_name and line.startswith(b'FONT'):
line = self.check_font(line)
self.xlfd_name = True
else:
for handler in self.handlers:
if line.startswith(handler[0]):
handler[1](line[len(handler[0]):].lstrip())
break
elif self.mode == MODE.PROPS:
if line.startswith(b'ENDPROPERTIES'):
self.mode = MODE.META
else:
line = self.check_prop(line)
else: # MODE.BITMAP
if line.startswith(b'ENDCHAR'):
self.mode = MODE.META
else:
self.check_bitmap(line)
return line
def read_check(self, line, callback):
match = re.search(br'^COMMENT\s*bdfcheck\s+(-.*)$', line)
if match and not self.options.parse(str(match[1], 'ascii'), True, self.parsed):
raise Exception('invalid bdfcheck directive')
line = callback(line)
return self.check_line(line) if line is not None else None
def read_lines(self, callback):
return fnio.InputFileStream.read_lines(self, lambda line: self.read_check(line, callback))
# -- Main --
def main_program(nonopt, parsed):
for input_name in nonopt or [None]:
InputFileStream(input_name, parsed).check()
if __name__ == '__main__':
fncli.start('bdfcheck.py', Options(), Params(), main_program)
+287
View File
@@ -0,0 +1,287 @@
/*
Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
const fncli = require('./fncli.js');
const fnio = require('./fnio.js');
const bdf = require('./bdf.js');
// -- Font --
class Font extends bdf.Font {
constructor() {
super();
this.minWidth = 0; // used in proportional()
this.avgWidth = 0;
}
_expand(char) {
if (char.dwidth.x >= 0) {
if (char.bbx.xoff >= 0) {
var width = Math.max(char.bbx.xoff + char.bbx.width, char.dwidth.x);
var dstXOff = char.bbx.xoff;
var expXOff = 0;
} else {
width = Math.max(char.bbx.width, char.dwidth.x - char.bbx.xoff);
dstXOff = 0;
expXOff = char.bbx.xoff;
}
} else {
const revXOff = char.bbx.xoff + char.bbx.width;
if (revXOff <= 0) {
width = -Math.min(char.dwidth.x, char.bbx.xoff);
dstXOff = width + char.bbx.xoff;
expXOff = -width;
} else {
width = Math.max(char.bbx.width, revXOff - char.dwidth.x);
dstXOff = width - char.bbx.width;
expXOff = revXOff - width;
}
}
const height = this.bbx.height;
if (width === char.bbx.width && height === char.bbx.height) {
return;
}
const srcRowSize = char.bbx.rowSize();
const dstRowSize = (width + 7) >> 3;
const dstYMax = this.pxAscender - char.bbx.yoff;
const dstYMin = dstYMax - char.bbx.height;
const copyRow = (dstXOff & 7) === 0;
const dstData = Buffer.alloc(dstRowSize * height);
for (let dstY = dstYMin; dstY < dstYMax; dstY++) {
let srcByteNo = (dstY - dstYMin) * srcRowSize;
let dstByteNo = dstY * dstRowSize + (dstXOff >> 3);
if (copyRow) {
char.data.copy(dstData, dstByteNo, srcByteNo, srcByteNo + srcRowSize);
} else {
let srcBitNo = 7;
let dstBitNo = 7 - (dstXOff & 7);
for (let x = 0; x < char.bbx.width; x++) {
if (char.data[srcByteNo] & (1 << srcBitNo)) {
dstData[dstByteNo] |= (1 << dstBitNo);
}
if (--srcBitNo < 0) {
srcBitNo = 7;
srcByteNo++;
}
if (--dstBitNo < 0) {
dstBitNo = 7;
dstByteNo++;
}
}
}
}
char.bbx = new bdf.BBX(width, height, expXOff, this.bbx.yoff);
char.props.set('BBX', char.bbx);
char.data = dstData;
}
expand() {
// PREXPAND / VERTICAL
const ascent = this.props.get('FONT_ASCENT');
const descent = this.props.get('FONT_DESCENT');
let pxAscent = (ascent == null ? 0 : fnutil.parseDec('FONT_ASCENT', ascent, 0, bdf.DPARSE_LIMIT));
let pxDescent = (descent == null ? 0 : fnutil.parseDec('FONT_DESCENT', descent, 0, bdf.DPARSE_LIMIT));
this.chars.forEach(char => {
pxAscent = Math.max(pxAscent, char.bbx.height + char.bbx.yoff);
pxDescent = Math.max(pxDescent, -char.bbx.yoff);
});
this.bbx.height = pxAscent + pxDescent;
this.bbx.yoff = -pxDescent;
// EXPAND / HORIZONTAL
let totalWidth = 0;
this.minWidth = this.chars[0].bbx.width;
this.chars.forEach(char => {
this._expand(char);
this.minWidth = Math.min(this.minWidth, char.bbx.width);
this.bbx.width = Math.max(this.bbx.width, char.bbx.width);
this.bbx.xoff = Math.min(this.bbx.xoff, char.bbx.xoff);
totalWidth += char.bbx.width;
});
this.avgWidth = fnutil.round(totalWidth / this.chars.length);
this.props.set('FONTBOUNDINGBOX', this.bbx);
}
expandX() {
this.chars.forEach(char => {
if (char.dwidth.x !== char.bbx.width) { // preserve SWIDTH if possible
char.swidth.x = fnutil.round(char.bbx.width * 1000 / this.bbx.height);
char.props.set('SWIDTH', char.swidth);
char.dwidth.x = char.bbx.width;
char.props.set('DWIDTH', char.dwidth);
}
char.bbx.xoff = 0;
char.props.set('BBX', char.bbx);
});
this.bbx.xoff = 0;
this.props.set('FONTBOUNDINGBOX', this.bbx);
}
expandY() {
const props = new Map([
[ 'FONT_ASCENT', this.pxAscender ],
[ 'FONT_DESCENT', -this.pxDescender ],
[ 'PIXEL_SIZE', this.bbx.height ]
]);
props.forEach((value, name) => {
if (this.props.get(name) != null) {
this.props.set(name, value);
}
});
this.xlfd[bdf.XLFD.PIXEL_SIZE] = this.bbx.height.toString();
this.props.set('FONT', this.xlfd.join('-'));
}
get proportional() {
return this.bbx.width > this.minWidth || super.proportional;
}
get pxAscender() {
return this.bbx.height + this.bbx.yoff;
}
get pxDescender() {
return this.bbx.yoff;
}
_read(input) {
super._read(input);
this.expand();
return this;
}
static read(input) {
return (new Font())._read(input);
}
_updateProp(name, value) {
if (this.props.get(name) != null) {
this.props.set(name, value);
}
}
}
// -- Export --
module.exports = Object.freeze({
Font
});
// -- Params --
class Params extends fncli.Params {
constructor() {
super();
this.expandX = false;
this.expandY = false;
this.output = null;
}
}
// -- Options --
const HELP = ('' +
'usage: bdfexp [-X] [-Y] [-o OUTPUT] [INPUT]\n' +
'Expand BDF font bitmaps\n' +
'\n' +
' -X zero xoffs, set character S/DWIDTH.X from the output\n' +
' BBX.width if needed\n' +
' -Y enlarge FONT_ASCENT, FONT_DESCENT and PIXEL_SIZE to\n' +
' cover the font bounding box, if needed\n' +
' -o OUTPUT output file (default = stdout)\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a BDF 2.1 font with unicode encoding.\n');
const VERSION = 'bdfexp 1.60, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE;
class Options extends fncli.Options {
constructor() {
super(['-o'], HELP, VERSION);
}
parse(name, value, params) {
switch (name) {
case '-X':
params.expandX = true;
break;
case '-Y':
params.expandY = true;
break;
case '-o':
params.output = value;
break;
default:
this.fallback(name, params);
}
}
}
// -- Main --
function mainProgram(nonopt, parsed) {
if (nonopt.length > 1) {
throw new Error('invalid number of arguments, try --help');
}
// READ INPUT
let ifs = new fnio.InputFileStream(nonopt[0]);
try {
var font = Font.read(ifs);
ifs.close();
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
// EXTRA ACTIONS
if (parsed.expandX) {
font.expandX();
}
if (parsed.expandY) {
font.expandY();
}
// WRITE OUTPUT
let ofs = new fnio.OutputFileStream(parsed.output);
try {
font.write(ofs);
ofs.close();
} catch (e) {
e.message = ofs.location() + e.message() + ofs.destroy();
throw e;
}
}
if (require.main === module) {
fncli.start('bdfexp.js', new Options(), new Params(), mainProgram);
}
+245
View File
@@ -0,0 +1,245 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from collections import OrderedDict
import fnutil
import fncli
import fnio
import bdf
# -- Font --
class Font(bdf.Font):
def __init__(self):
bdf.Font.__init__(self)
self.min_width = 0 # used in proportional()
self.avg_width = 0
def _expand(self, char):
if char.dwidth.x >= 0:
if char.bbx.xoff >= 0:
width = max(char.bbx.xoff + char.bbx.width, char.dwidth.x)
dst_xoff = char.bbx.xoff
exp_xoff = 0
else:
width = max(char.bbx.width, char.dwidth.x - char.bbx.xoff)
dst_xoff = 0
exp_xoff = char.bbx.xoff
else:
rev_xoff = char.bbx.xoff + char.bbx.width
if rev_xoff <= 0:
width = -min(char.dwidth.x, char.bbx.xoff)
dst_xoff = width + char.bbx.xoff
exp_xoff = -width
else:
width = max(char.bbx.width, rev_xoff - char.dwidth.x)
dst_xoff = width - char.bbx.width
exp_xoff = rev_xoff - width
height = self.bbx.height
if width == char.bbx.width and height == char.bbx.height:
return
src_row_size = char.bbx.row_size()
dst_row_size = (width + 7) >> 3
dst_ymax = self.px_ascender - char.bbx.yoff
dst_ymin = dst_ymax - char.bbx.height
copy_row = (dst_xoff & 7) == 0
dst_data = bytearray(dst_row_size * height)
for dst_y in range(dst_ymin, dst_ymax):
src_byte_no = (dst_y - dst_ymin) * src_row_size
dst_byte_no = dst_y * dst_row_size + (dst_xoff >> 3)
if copy_row:
dst_data[dst_byte_no : dst_byte_no + src_row_size] = \
char.data[src_byte_no : src_byte_no + src_row_size]
else:
src_bit_no = 7
dst_bit_no = 7 - (dst_xoff & 7)
for _ in range(0, char.bbx.width):
if char.data[src_byte_no] & (1 << src_bit_no):
dst_data[dst_byte_no] |= (1 << dst_bit_no)
if src_bit_no > 0:
src_bit_no -= 1
else:
src_bit_no = 7
src_byte_no += 1
if dst_bit_no > 0:
dst_bit_no -= 1
else:
dst_bit_no = 7
dst_byte_no += 1
char.bbx = bdf.BBX(width, height, exp_xoff, self.bbx.yoff)
char.props.set('BBX', char.bbx)
char.data = dst_data
def expand(self):
# PREXPAND / VERTICAL
ascent = self.props.get('FONT_ASCENT')
descent = self.props.get('FONT_DESCENT')
px_ascent = 0 if ascent is None else fnutil.parse_dec('FONT_ASCENT', ascent, 0, bdf.DPARSE_LIMIT)
px_descent = 0 if descent is None else fnutil.parse_dec('FONT_DESCENT', descent, 0, bdf.DPARSE_LIMIT)
for char in self.chars:
px_ascent = max(px_ascent, char.bbx.height + char.bbx.yoff)
px_descent = max(px_descent, -char.bbx.yoff)
self.bbx.height = px_ascent + px_descent
self.bbx.yoff = -px_descent
# EXPAND / HORIZONTAL
total_width = 0
self.min_width = self.chars[0].bbx.width
for char in self.chars:
self._expand(char)
self.min_width = min(self.min_width, char.bbx.width)
self.bbx.width = max(self.bbx.width, char.bbx.width)
self.bbx.xoff = min(self.bbx.xoff, char.bbx.xoff)
total_width += char.bbx.width
self.avg_width = round(total_width / len(self.chars))
self.props.set('FONTBOUNDINGBOX', self.bbx)
def expand_x(self):
for char in self.chars:
if char.dwidth.x != char.bbx.width:
char.swidth.x = round(char.bbx.width * 1000 / self.bbx.height)
char.props.set('SWIDTH', char.swidth)
char.dwidth.x = char.bbx.width
char.props.set('DWIDTH', char.dwidth)
char.bbx.xoff = 0
char.props.set('BBX', char.bbx)
self.bbx.xoff = 0
self.props.set('FONTBOUNDINGBOX', self.bbx)
def expand_y(self):
props = OrderedDict((
('FONT_ASCENT', self.px_ascender),
('FONT_DESCENT', -self.px_descender),
('PIXEL_SIZE', self.bbx.height)
))
for [name, value] in props.items():
if self.props.get(name) is not None:
self.props.set(name, value)
self.xlfd[bdf.XLFD.PIXEL_SIZE] = bytes(str(self.bbx.height), 'ascii')
self.props.set('FONT', b'-'.join(self.xlfd))
@property
def proportional(self):
return self.bbx.width > self.min_width or bdf.Font.proportional.fget(self) # pylint: disable=no-member
@property
def px_ascender(self):
return self.bbx.height + self.bbx.yoff
@property
def px_descender(self):
return self.bbx.yoff
def _read(self, input):
bdf.Font._read(self, input)
self.expand()
return self
@staticmethod
def read(input):
return Font()._read(input) # pylint: disable=protected-access
# -- Params --
class Params(fncli.Params):
def __init__(self):
fncli.Params.__init__(self)
self.expand_x = False
self.expand_y = False
self.output_name = None
# -- Options --
HELP = ('' +
'usage: bdfexp [-X] [-Y] [-o OUTPUT] [INPUT]\n' +
'Expand BDF font bitmaps\n' +
'\n' +
' -X zero xoffs, set character S/DWIDTH.X from the output\n' +
' BBX.width if needed\n' +
' -Y enlarge FONT_ASCENT, FONT_DESCENT and PIXEL_SIZE to\n' +
' cover the font bounding box, if needed\n' +
' -o OUTPUT output file (default = stdout)\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a BDF 2.1 font with unicode encoding.\n')
VERSION = 'bdfexp 1.62, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE
class Options(fncli.Options):
def __init__(self):
fncli.Options.__init__(self, ['-o'], HELP, VERSION)
def parse(self, name, value, params):
if name == '-X':
params.expand_x = True
elif name == '-Y':
params.expand_y = True
elif name == '-o':
params.output_name = value
else:
self.fallback(name, params)
# -- Main --
def main_program(nonopt, parsed):
if len(nonopt) > 1:
raise Exception('invalid number of arguments, try --help')
# READ INPUT
font = fnio.read_file(nonopt[0] if nonopt else None, Font.read)
# EXTRA ACTIONS
if parsed.expand_x:
font.expand_x()
if parsed.expand_y:
font.expand_y()
# WRITE OUTPUT
fnio.write_file(parsed.output_name, lambda ofs: font.write(ofs))
if __name__ == '__main__':
fncli.start('bdfexp.py', Options(), Params(), main_program)
+256
View File
@@ -0,0 +1,256 @@
/*
Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
const fncli = require('./fncli.js');
const fnio = require('./fnio.js');
const bdf = require('./bdf.js');
const bdfexp = require('./bdfexp.js');
// -- Params --
class Params extends fncli.Params {
constructor() {
super();
this.charSet = -1;
this.minChar = -1;
this.fntFamily = 0;
this.output = null;
}
}
// -- Options --
const HELP = ('' +
'usage: bdftofnt [-c CHARSET] [-m MINCHAR] [-f FAMILY] [-o OUTPUT] [INPUT]\n' +
'Convert a BDF font to Windows FNT\n' +
'\n' +
' -c CHARSET fnt character set (default = 0, see wingdi.h ..._CHARSET)\n' +
' -m MINCHAR fnt minimum character code (8-bit CP decimal, not unicode)\n' +
' -f FAMILY fnt family: DontCare, Roman, Swiss, Modern or Decorative\n' +
' -o OUTPUT output file (default = stdout, may not be a terminal)\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a BDF 2.1 font with unicode encoding.\n');
const VERSION = 'bdftofnt 1.60, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE;
const FNT_FAMILIES = [ 'DontCare', 'Roman', 'Swiss', 'Modern', 'Decorative' ];
class Options extends fncli.Options {
constructor() {
super(['-c', '-m', '-f', '-o'], HELP, VERSION);
}
parse(name, value, params) {
switch (name) {
case '-c':
params.charSet = fnutil.parseDec('CHARSET', value, 0, 255);
break;
case '-m':
params.minChar = fnutil.parseDec('MINCHAR', value, 0, 255);
break;
case '-f':
params.fntFamily = FNT_FAMILIES.indexOf(value);
if (params.fntFamily === -1) {
throw new Error('invalid FAMILY');
}
break;
case '-o':
params.output = value;
break;
default:
this.fallback(name, params);
}
}
}
// -- Main --
const FNT_HEADER_SIZE = 118;
const FNT_CHARSETS = [238, 204, 0, 161, 162, 177, 178, 186, 163];
function mainProgram(nonopt, parsed) {
if (nonopt.length > 1) {
throw new Error('invalid number of arguments, try --help');
}
let charSet = parsed.charSet;
let minChar = parsed.minChar;
// READ INPUT
let ifs = new fnio.InputFileStream(nonopt[0]);
try {
var font = bdfexp.Font.read(ifs);
ifs.close();
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
// COMPUTE
if (charSet === -1) {
const encoding = font.xlfd[bdf.XLFD.CHARSET_ENCODING];
if (encoding.toLowerCase().match(/^(cp)?125[0-8]$/)) {
charSet = FNT_CHARSETS[parseInt(encoding.substring(encoding.length - 1), 10)];
} else {
charSet = 255;
}
}
try {
const numChars = font.chars.length;
if (numChars > 256) {
throw new Error('too many characters, the maximum is 256');
}
if (minChar === -1) {
if (numChars === 192 || numChars === 256) {
minChar = 256 - numChars;
} else {
minChar = font.chars[0].code;
}
}
var maxChar = minChar + numChars - 1;
if (maxChar >= 256) {
throw new Error('the maximum character code is too big, (re)specify -m');
}
// HEADER
var vtell = FNT_HEADER_SIZE + (numChars + 1) * 4;
var bitsOffset = vtell;
var ctable = [];
var widthBytes = 0;
// CTABLE/GLYPHS
font.chars.forEach(char => {
const rowSize = char.bbx.rowSize();
ctable.push(char.bbx.width);
ctable.push(vtell);
vtell += rowSize * font.bbx.height;
widthBytes += rowSize;
});
if (vtell > 0xFFFF) {
throw new Error('too much character data');
}
// SENTINEL
var sentinel = 2 - widthBytes % 2;
ctable.push(sentinel * 8);
ctable.push(vtell);
vtell += sentinel * font.bbx.height;
widthBytes += sentinel;
if (widthBytes > 0xFFFF) {
throw new Error('the total character width is too big');
}
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
// WRITE
let ofs = new fnio.OutputFileStream(parsed.output, null);
try {
// HEADER
const family = font.xlfd[bdf.XLFD.FAMILY_NAME];
let copyright = font.props.get('COPYRIGHT');
copyright = (copyright != null) ? fnutil.unquote(copyright).substring(0, 60) : '';
ofs.write16(0x0200); // font version
ofs.write32(vtell + family.length + 1); // total size
ofs.writeZStr(copyright, 60 - copyright.length);
ofs.write16(0); // gdi, device type
ofs.write16(fnutil.round(font.bbx.height * 72 / 96));
ofs.write16(96); // vertical resolution
ofs.write16(96); // horizontal resolution
ofs.write16(font.pxAscender); // base line
ofs.write16(0); // internal leading
ofs.write16(0); // external leading
ofs.write8(Number(font.italic));
ofs.write8(0); // underline
ofs.write8(0); // strikeout
ofs.write16(font.bold ? 700 : 400);
ofs.write8(charSet);
ofs.write16(font.proportional ? 0 : font.bbx.width);
ofs.write16(font.bbx.height);
ofs.write8((parsed.fntFamily << 4) + Number(font.proportional));
ofs.write16(font.avgWidth);
ofs.write16(font.bbx.width);
ofs.write8(minChar);
ofs.write8(maxChar);
let defaultIndex = maxChar - minChar;
let breakIndex = 0;
if (font.defaultCode !== -1) {
defaultIndex = font.chars.findIndex(char => char.code === font.defaultCode);
}
if (minChar <= 0x20 && maxChar >= 0x20) {
breakIndex = 0x20 - minChar;
}
ofs.write8(defaultIndex);
ofs.write8(breakIndex);
ofs.write16(widthBytes);
ofs.write32(0); // device name
ofs.write32(vtell);
ofs.write32(0); // gdi bits pointer
ofs.write32(bitsOffset);
ofs.write8(0); // reserved
// CTABLE
ctable.forEach(value => ofs.write16(value));
// GLYPHS
const data = Buffer.alloc(font.bbx.height * font.bbx.rowSize());
font.chars.forEach(char => {
const rowSize = char.bbx.rowSize();
let counter = 0;
// MS coordinates
for (let n = 0; n < rowSize; n++) {
for (let y = 0; y < font.bbx.height; y++) {
data[counter++] = char.data[rowSize * y + n];
}
}
ofs.write(data.slice(0, counter));
});
ofs.write(Buffer.alloc(sentinel * font.bbx.height));
// FAMILY
ofs.writeZStr(family, 1);
ofs.close();
} catch (e) {
e.message = ofs.location() + e.message + ofs.destroy();
throw e;
}
}
if (require.main === module) {
fncli.start('bdftofnt.js', new Options(), new Params(), mainProgram);
}
+222
View File
@@ -0,0 +1,222 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import re
import fnutil
import fncli
import fnio
import bdf
import bdfexp
# -- Params --
class Params(fncli.Params):
def __init__(self):
fncli.Params.__init__(self)
self.char_set = -1
self.min_char = -1
self.fnt_family = 0
self.output_name = None
# -- Options --
HELP = ('' +
'usage: bdftofnt [-c CHARSET] [-m MINCHAR] [-f FAMILY] [-o OUTPUT] [INPUT]\n' +
'Convert a BDF font to Windows FNT\n' +
'\n' +
' -c CHARSET fnt character set (default = 0, see wingdi.h ..._CHARSET)\n' +
' -m MINCHAR fnt minimum character code (8-bit CP decimal, not unicode)\n' +
' -f FAMILY fnt family: DontCare, Roman, Swiss, Modern or Decorative\n' +
' -o OUTPUT output file (default = stdout, may not be a terminal)\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a BDF 2.1 font with unicode encoding.\n')
VERSION = 'bdftofnt 1.62, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE
FNT_FAMILIES = ['DontCare', 'Roman', 'Swiss', 'Modern', 'Decorative']
class Options(fncli.Options):
def __init__(self):
fncli.Options.__init__(self, ['-c', '-m', '-f', '-o'], HELP, VERSION)
def parse(self, name, value, params):
if name == '-c':
params.char_set = fnutil.parse_dec('CHARSET', value, 0, 255)
elif name == '-m':
params.min_char = fnutil.parse_dec('MINCHAR', value, 0, 255)
elif name == '-f':
if value in FNT_FAMILIES:
params.fnt_family = FNT_FAMILIES.index(value)
else:
raise Exception('invalid FAMILY')
elif name == '-o':
params.output_name = value
else:
self.fallback(name, params)
# -- Main --
FNT_HEADER_SIZE = 118
FNT_CHARSETS = [238, 204, 0, 161, 162, 177, 178, 186, 163]
def main_program(nonopt, parsed):
if len(nonopt) > 1:
raise Exception('invalid number of arguments, try --help')
char_set = parsed.char_set
min_char = parsed.min_char
# READ INPUT
ifs = fnio.InputFileStream(nonopt[0] if nonopt else None)
font = ifs.process(bdfexp.Font.read)
# COMPUTE
if char_set == -1:
encoding = font.xlfd[bdf.XLFD.CHARSET_ENCODING]
if re.fullmatch(b'(cp)?125[0-8]', encoding.lower()):
char_set = FNT_CHARSETS[int(encoding[-1:])]
else:
char_set = 255
try:
num_chars = len(font.chars)
if num_chars > 256:
raise Exception('too many characters, the maximum is 256')
if min_char == -1:
if num_chars in [192, 256]:
min_char = 256 - num_chars
else:
min_char = font.chars[0].code
max_char = min_char + num_chars - 1
if max_char >= 256:
raise Exception('the maximum character code is too big, (re)specify -m')
# HEADER
vtell = FNT_HEADER_SIZE + (num_chars + 1) * 4
bits_offset = vtell
ctable = []
width_bytes = 0
# CTABLE/GLYPHS
for char in font.chars:
row_size = char.bbx.row_size()
ctable.append(char.bbx.width)
ctable.append(vtell)
vtell += row_size * font.bbx.height
width_bytes += row_size
if vtell > 0xFFFF:
raise Exception('too much character data')
# SENTINEL
sentinel = 2 - width_bytes % 2
ctable.append(sentinel * 8)
ctable.append(vtell)
vtell += sentinel * font.bbx.height
width_bytes += sentinel
if width_bytes > 0xFFFF:
raise Exception('the total character width is too big')
except Exception as ex:
ex.message = ifs.location() + getattr(ex, 'message', str(ex))
raise
# WRITE
def write_fnt(output):
# HEADER
family = font.xlfd[bdf.XLFD.FAMILY_NAME]
copyright = font.props.get('COPYRIGHT')
copyright = fnutil.unquote(copyright)[:60] if copyright is not None else b''
output.write16(0x0200) # font version
output.write32(vtell + len(family) + 1) # total size
output.write_zstr(copyright, 60 - len(copyright))
output.write16(0) # gdi, device type
output.write16(round(font.bbx.height * 72 / 96))
output.write16(96) # vertical resolution
output.write16(96) # horizontal resolution
output.write16(font.px_ascender) # base line
output.write16(0) # internal leading
output.write16(0) # external leading
output.write8(int(font.italic))
output.write8(0) # underline
output.write8(0) # strikeout
output.write16(700 if font.bold else 400)
output.write8(char_set)
output.write16(0 if font.proportional else font.bbx.width)
output.write16(font.bbx.height)
output.write8((parsed.fnt_family << 4) + int(font.proportional))
output.write16(font.avg_width)
output.write16(font.bbx.width)
output.write8(min_char)
output.write8(max_char)
default_index = max_char - min_char
break_index = 0
if font.default_code != -1:
default_index = next(index for index, char in enumerate(font.chars) if char.code == font.default_code)
if min_char <= 0x20 <= max_char:
break_index = 0x20 - min_char
output.write8(default_index)
output.write8(break_index)
output.write16(width_bytes)
output.write32(0) # device name
output.write32(vtell)
output.write32(0) # gdi bits pointer
output.write32(bits_offset)
output.write8(0) # reserved
# CTABLE
for value in ctable:
output.write16(value)
# GLYPHS
data = bytearray(font.bbx.height * font.bbx.row_size())
for char in font.chars:
row_size = char.bbx.row_size()
counter = 0
# MS coordinates
for n in range(0, row_size):
for y in range(0, font.bbx.height):
data[counter] = char.data[row_size * y + n]
counter += 1
output.write(data[:counter])
output.write(bytes(sentinel * font.bbx.height))
# FAMILY
output.write_zstr(family, 1)
fnio.write_file(parsed.output_name, write_fnt, encoding=None)
if __name__ == '__main__':
fncli.start('bdftofnt.py', Options(), Params(), main_program)
+293
View File
@@ -0,0 +1,293 @@
/*
Copyright (C) 2017-2019 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
const fncli = require('./fncli.js');
const fnio = require('./fnio.js');
const bdfexp = require('./bdfexp.js');
// -- Params --
class Params extends fncli.Params {
constructor() {
super();
this.version = -1;
this.exchange = -1;
this.output = null;
}
}
// -- Options --
const HELP = ('' +
'usage: bdftopsf [-1|-2|-r] [-g|-G] [-o OUTPUT] [INPUT.bdf] [TABLE...]\n' +
'Convert a BDF font to PC Screen Font or raw font\n' +
'\n' +
' -1, -2 write a PSF version 1 or 2 font (default = 1 if possible)\n' +
' -r, --raw write a RAW font\n' +
' -g, --vga exchange the characters at positions 0...31 with these at\n' +
' 192...223 (default for VGA text mode compliant PSF fonts\n' +
' with 224 to 512 characters starting with unicode 00A3)\n' +
' -G do not exchange characters 0...31 and 192...223\n' +
' -o OUTPUT output file (default = stdout, may not be a terminal)\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a monospaced unicode-encoded BDF 2.1 font.\n' +
'\n' +
'The tables are text files with two or more hexadecimal unicodes per line:\n' +
'a character code from the BDF, and extra code(s) for it. All extra codes\n' +
'are stored sequentially in the PSF unicode table for their character.\n' +
'<ss> is always specified as FFFE, although it is stored as FE in PSF2.\n');
const VERSION = 'bdftopsf 1.58, Copyright (C) 2017-2019 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_VERSION;
class Options extends fncli.Options {
constructor() {
super(['-o'], HELP, VERSION);
}
parse(name, value, params) {
switch (name) {
case '-1':
case '-2':
params.version = parseInt(name[1]);
break;
case '-r':
case '--raw':
params.version = 0;
break;
case '-g':
case '--vga':
params.exchange = true;
break;
case '-G':
params.exchange = false;
break;
case '-o':
params.output = value;
break;
default:
this.fallback(name, params);
}
}
}
// -- Main --
function mainProgram(nonopt, parsed) {
const bdfile = nonopt.length > 0 && nonopt[0].toLowerCase().endsWith('.bdf');
let version = parsed.version;
let exchange = parsed.exchange;
let ver1Unicodes = true;
// READ INPUT
let ifs = new fnio.InputFileStream(bdfile ? nonopt[0] : null);
try {
var font = bdfexp.Font.read(ifs);
ifs.close();
font.chars.forEach(char => {
const prefix = `char ${char.code}: `;
if (char.bbx.width !== font.bbx.width) {
throw new Error(prefix + 'output width not equal to maximum output width');
}
if (char.code === 65534) {
throw new Error(prefix + 'not a character, use 65535 for empty position');
}
if (char.code >= 65536) {
if (version === 1) {
throw new Error(prefix + '-1 requires unicodes <= 65535');
}
ver1Unicodes = false;
}
});
// VERSION
var ver1NumChars = (font.chars.length === 256 || font.chars.length === 512);
if (version === 1) {
if (!ver1NumChars) {
throw new Error('-1 requires a font with 256 or 512 characters');
}
if (font.bbx.width !== 8) {
throw new Error('-1 requires a font with width 8');
}
}
// EXCHANGE
var vgaNumChars = font.chars.length >= 224 && font.chars.length <= 512;
var vgaTextSize = font.bbx.width === 8 && [8, 14, 16].indexOf(font.bbx.height) !== -1;
if (exchange === true) {
if (!vgaNumChars) {
throw new Error('-g/--vga requires a font with 224...512 characters');
}
if (!vgaTextSize) {
throw new Error('-g/--vga requires an 8x8, 8x14 or 8x16 font');
}
}
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
// READ TABLES
let tables = [];
function loadExtra(line) {
const words = line.split(/\s+/);
if (words.length < 2) {
throw new Error('invalid format');
}
const uni = fnutil.parseHex('unicode', words[0]);
let table = tables[uni];
if (uni === 0xFFFE) {
throw new Error('FFFE is not a character');
}
if (font.chars.findIndex(char => char.code === uni) !== -1) {
if (uni > fnutil.UNICODE_BMP_MAX) {
ver1Unicodes = false;
}
if (table == null) {
table = tables[uni] = [];
}
words.slice(1).forEach(word => {
const dup = fnutil.parseHex('extra code', word);
if (dup === 0xFFFF) {
throw new Error('FFFF is not a character');
}
if (dup > fnutil.UNICODE_BMP_MAX) {
ver1Unicodes = false;
}
if (table.indexOf(dup) === -1 || table.indexOf(0xFFFE) !== -1) {
table.push(dup);
}
});
if (version === 1 && !ver1Unicodes) {
throw new Error('-1 requires unicodes <= ' + fnutil.UNICODE_BMP_MAX.toString(16));
}
}
}
nonopt.slice(Number(bdfile)).forEach(name => {
ifs = new fnio.InputFileStream(name);
try {
ifs.readLines(loadExtra);
ifs.close();
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
});
// VERSION
if (version === -1) {
version = ver1NumChars && ver1Unicodes && font.bbx.width === 8 ? 1 : 2;
}
// EXCHANGE
if (exchange === -1) {
exchange = vgaTextSize && version >= 1 && vgaNumChars && font.chars[0].code === 0x00A3;
}
if (exchange) {
const control = font.chars.splice(0, 32, ...font.chars.splice(192, 32));
font.chars.splice(192, 0, ...control);
}
// WRITE
let ofs = new fnio.OutputFileStream(parsed.output, null);
try {
// HEADER
if (version === 1) {
ofs.write8(0x36);
ofs.write8(0x04);
ofs.write8((font.chars.length >> 8) + 1);
ofs.write8(font.bbx.height);
} else if (version === 2) {
ofs.write32(0x864AB572);
ofs.write32(0x00000000);
ofs.write32(0x00000020);
ofs.write32(0x00000001);
ofs.write32(font.chars.length);
ofs.write32(font.chars[0].data.length);
ofs.write32(font.bbx.height);
ofs.write32(font.bbx.width);
}
// GLYPHS
font.chars.forEach(char => ofs.write(char.data));
// UNICODES
if (version > 0) {
const writeUnicode = function(code) {
if (version === 1) {
ofs.write16(code);
} else if (code <= 0x7F) {
ofs.write8(code);
} else if (code === 0xFFFE || code === 0xFFFF) {
ofs.write8(code & 0xFF);
} else {
if (code <= 0x7FF) {
ofs.write8(0xC0 + (code >> 6));
} else {
if (code <= 0xFFFF) {
ofs.write8(0xE0 + (code >> 12));
} else {
ofs.write8(0xF0 + (code >> 18));
ofs.write8(0x80 + ((code >> 12) & 0x3F));
}
ofs.write8(0x80 + ((code >> 6) & 0x3F));
}
ofs.write8(0x80 + (code & 0x3F));
}
};
font.chars.forEach(char => {
if (char.code !== 0xFFFF) {
writeUnicode(char.code);
}
if (tables[char.code] != null) {
tables[char.code].forEach(extra => writeUnicode(extra));
}
writeUnicode(0xFFFF);
});
}
// FINISH
ofs.close();
} catch (e) {
e.message = ofs.location() + e.message + ofs.destroy();
throw e;
}
}
if (require.main === module) {
fncli.start('bdftopsf.js', new Options(), new Params(), mainProgram);
}
+241
View File
@@ -0,0 +1,241 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import fnutil
import fncli
import fnio
import bdfexp
# -- Params --
class Params(fncli.Params):
def __init__(self):
fncli.Params.__init__(self)
self.version = -1
self.exchange = -1
self.output_name = None
# -- Options --
HELP = ('' +
'usage: bdftopsf [-1|-2|-r] [-g|-G] [-o OUTPUT] [INPUT.bdf] [TABLE...]\n' +
'Convert a BDF font to PC Screen Font or raw font\n' +
'\n' +
' -1, -2 write a PSF version 1 or 2 font (default = 1 if possible)\n' +
' -r, --raw write a RAW font\n' +
' -g, --vga exchange the characters at positions 0...31 with these at\n' +
' 192...223 (default for VGA text mode compliant PSF fonts\n' +
' with 224 to 512 characters starting with unicode 00A3)\n' +
' -G do not exchange characters 0...31 and 192...223\n' +
' -o OUTPUT output file (default = stdout, may not be a terminal)\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a monospaced unicode-encoded BDF 2.1 font.\n' +
'\n' +
'The tables are text files with two or more hexadecimal unicodes per line:\n' +
'a character code from the BDF, and extra code(s) for it. All extra codes\n' +
'are stored sequentially in the PSF unicode table for their character.\n' +
'<ss> is always specified as FFFE, although it is stored as FE in PSF2.\n')
VERSION = 'bdftopsf 1.62, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE
class Options(fncli.Options):
def __init__(self):
fncli.Options.__init__(self, ['-o'], HELP, VERSION)
def parse(self, name, value, params):
if name in ['-1', '-2']:
params.version = int(name[1])
elif name in ['-r', '--raw']:
params.version = 0
elif name in ['-g', '--vga']:
params.exchange = True
elif name == '-G':
params.exchange = False
elif name == '-o':
params.output_name = value
else:
self.fallback(name, params)
# -- Main --
def main_program(nonopt, parsed):
version = parsed.version
exchange = parsed.exchange
bdfile = len(nonopt) > 0 and nonopt[0].lower().endswith('.bdf')
ver1_unicodes = True
# READ INPUT
ifs = fnio.InputFileStream(nonopt[0] if bdfile else None)
font = ifs.process(bdfexp.Font.read)
try:
for char in font.chars:
prefix = 'char %d: ' % char.code
if char.bbx.width != font.bbx.width:
raise Exception(prefix + 'output width not equal to maximum output width')
if char.code == 65534:
raise Exception(prefix + 'not a character, use 65535 for empty position')
if char.code >= 65536:
if version == 1:
raise Exception(prefix + '-1 requires unicodes <= 65535')
ver1_unicodes = False
# VERSION
ver1_num_chars = len(font.chars) == 256 or len(font.chars) == 512
if version == 1:
if not ver1_num_chars:
raise Exception('-1 requires a font with 256 or 512 characters')
if font.bbx.width != 8:
raise Exception('-1 requires a font with width 8')
# EXCHANGE
vga_num_chars = len(font.chars) >= 224 and len(font.chars) <= 512
vga_text_size = font.bbx.width == 8 and font.bbx.height in [8, 14, 16]
if exchange is True:
if not vga_num_chars:
raise Exception('-g/--vga requires a font with 224...512 characters')
if not vga_text_size:
raise Exception('-g/--vga requires an 8x8, 8x14 or 8x16 font')
except Exception as ex:
ex.message = ifs.location() + getattr(ex, 'message', str(ex))
raise
# READ TABLES
tables = dict()
def load_extra(line):
nonlocal ver1_unicodes
words = line.split()
if len(words) < 2:
raise Exception('invalid format')
uni = fnutil.parse_hex('unicode', words[0])
if uni == 0xFFFE:
raise Exception('FFFE is not a character')
if next((char for char in font.chars if char.code == uni), None):
if uni > fnutil.UNICODE_BMP_MAX:
ver1_unicodes = False
if uni not in tables:
tables[uni] = []
table = tables[uni]
for word in words[1:]:
dup = fnutil.parse_hex('extra code', word)
if dup == 0xFFFF:
raise Exception('FFFF is not a character')
if dup > fnutil.UNICODE_BMP_MAX:
ver1_unicodes = False
if not dup in table or 0xFFFE in table:
tables[uni].append(dup)
if version == 1 and not ver1_unicodes:
raise Exception('-1 requires unicodes <= %X' % fnutil.UNICODE_BMP_MAX)
for table_name in nonopt[int(bdfile):]:
fnio.read_file(table_name, lambda ifs: ifs.read_lines(load_extra))
# VERSION
if version == -1:
version = 1 if ver1_num_chars and ver1_unicodes and font.bbx.width == 8 else 2
# EXCHANGE
if exchange == -1:
exchange = vga_text_size and version >= 1 and vga_num_chars and font.chars[0].code == 0x00A3
if exchange:
font.chars = font.chars[192:224] + font.chars[32:192] + font.chars[0:32] + font.chars[224:]
# WRITE
def write_psf(output):
# HEADER
if version == 1:
output.write8(0x36)
output.write8(0x04)
output.write8((len(font.chars) >> 8) + 1)
output.write8(font.bbx.height)
elif version == 2:
output.write32(0x864AB572)
output.write32(0x00000000)
output.write32(0x00000020)
output.write32(0x00000001)
output.write32(len(font.chars))
output.write32(len(font.chars[0].data))
output.write32(font.bbx.height)
output.write32(font.bbx.width)
# GLYPHS
for char in font.chars:
output.write(char.data)
# UNICODES
if version > 0:
def write_unicode(code):
if version == 1:
output.write16(code)
elif code <= 0x7F:
output.write8(code)
elif code in [0xFFFE, 0xFFFF]:
output.write8(code & 0xFF)
else:
if code <= 0x7FF:
output.write8(0xC0 + (code >> 6))
else:
if code <= 0xFFFF:
output.write8(0xE0 + (code >> 12))
else:
output.write8(0xF0 + (code >> 18))
output.write8(0x80 + ((code >> 12) & 0x3F))
output.write8(0x80 + ((code >> 6) & 0x3F))
output.write8(0x80 + (code & 0x3F))
for char in font.chars:
if char.code != 0xFFFF:
write_unicode(char.code)
if char.code in tables:
for extra in tables[char.code]:
write_unicode(extra)
write_unicode(0xFFFF)
fnio.write_file(parsed.output_name, write_psf, encoding=None)
if __name__ == '__main__':
fncli.start('bdftopsf.py', Options(), Params(), main_program)
+180
View File
@@ -0,0 +1,180 @@
/*
Copyright (C) 2018-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
// -- Params --
class Params {
constructor() {
this.excstk = false;
}
}
// -- Options --
class Options {
constructor(needArgs, helpText, versionText) {
needArgs.forEach(name => {
if (!name.match(/^(-[^-]|--[^=]+)$/)) {
throw new Error(`invalid option name "${name}"`);
}
});
this.needArgs = needArgs;
this.helpText = helpText;
this.versionText = versionText;
}
posixlyCorrect() { // eslint-disable-line class-methods-use-this
return process.env['POSIXLY_CORRECT'] != null;
}
needsArg(name) {
return this.needArgs.includes(name);
}
fallback(name, params) {
if (name === '--excstk') {
params.excstk = true;
} else if (name === '--help' && this.helpText != null) {
process.stdout.write(this.helpText);
process.exit(0);
} else if (name === '--version' && this.versionText != null) {
process.stdout.write(this.versionText);
process.exit(0);
} else {
let suffix = this.needsArg(name) ? ' (taking an argument?)' : '';
suffix += (this.helpText != null) ? ', try --help' : '';
throw new Error(`unknown option "${name}"${suffix}`);
}
}
reader(args, skip = 2) {
return new Options.Reader(this, args, skip);
}
}
Options.Reader = class {
constructor(options, args, skip) {
this.options = options;
this.args = args;
this.skip = skip;
}
forEach(callback) {
let optind;
for (optind = this.skip; optind < this.args.length; optind++) {
let arg = this.args[optind];
if (arg === '-' || !arg.startsWith('-')) {
if (this.options.posixlyCorrect()) {
break;
}
callback(null, arg);
} else if (arg === '--') {
optind++;
break;
} else {
let name, value;
if (!arg.startsWith('--')) {
for (;;) {
name = arg.substring(0, 2);
value = (name !== arg) ? arg.substring(2) : null;
if (this.options.needsArg(name) || value == null) {
break;
}
callback(name, null);
arg = '-' + value;
}
} else if (arg.indexOf('=') >= 3) {
name = arg.split('=', 1)[0];
if (!this.options.needsArg(name)) {
throw new Error(`option "${name}" does not take an argument`);
}
value = arg.substring(name.length + 1);
} else {
name = arg;
value = null;
}
if (value == null && Number(this.options.needsArg(name)) > 0) {
if (++optind === this.args.length) {
throw new Error(`option "${name}" requires an argument`);
}
value = this.args[optind];
}
callback(name, value);
}
}
this.args.slice(optind).forEach(value => callback(null, value));
}
};
Object.defineProperty(Options, 'Reader', { 'enumerable': false });
Object.defineProperty(Options.Reader, 'name', { value: 'Reader' });
// -- Main --
function start(programName, options, params, mainProgram) { // eslint-disable-line consistent-return
const parsed = (params != null) ? params : new Params();
try {
const version = process.version.match(/^v?(\d+)\.(\d+)/);
if (version.length < 3) {
throw new Error('unable to obtain node version');
} else if ((parseInt(version[1]) * 1000 + parseInt(version[2])) < 6009) {
throw new Error('node version 6.9.0 or later required');
}
if (params == null) {
return mainProgram(options.reader(process.argv), name => options.fallback(name, parsed));
} else {
let nonopt = [];
options.reader(process.argv).forEach((name, value) => {
if (name == null) {
nonopt.push(value);
} else {
options.parse(name, value, parsed);
}
});
return mainProgram(nonopt, parsed);
}
} catch (e) {
if (parsed.excstk) {
if (e.stack != null) {
process.stderr.write(e.stack + '\n');
} else {
throw e;
}
} else {
process.stderr.write(`${process.argv.length >= 2 ? process.argv[1] : programName}: ${e.message}\n`);
}
process.exit(1);
}
}
// -- Exports --
module.exports = Object.freeze({
Params,
Options,
start
});
+162
View File
@@ -0,0 +1,162 @@
#
# Copyright (C) 2018-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import sys
import os
import re
# -- Params --
class Params:
def __init__(self):
self.excstk = False
# -- Options --
class Options:
def __init__(self, need_args, help_text, version_text):
for name in need_args:
if not re.fullmatch('(-[^-]|--[^=]+)', name):
raise Exception('invalid option name "%s"' % name)
self.need_args = need_args
self.help_text = help_text
self.version_text = version_text
def posixly_correct(self): # pylint: disable=no-self-use
return 'POSIXLY_CORRECT' in os.environ
def needs_arg(self, name):
return name in self.need_args
def fallback(self, name, params):
if name == '--excstk':
params.excstk = True
elif name == '--help' and self.help_text is not None:
sys.stdout.write(self.help_text)
sys.exit(0)
elif name == '--version' and self.version_text is not None:
sys.stdout.write(self.version_text)
sys.exit(0)
else:
suffix = ' (taking an argument?)' if self.needs_arg(name) else ''
suffix += ', try --help' if self.help_text is not None else ''
raise Exception('unknown option "%s"%s' % (name, suffix))
def reader(self, args, skip_zero=True):
return Options.Reader(self, args, skip_zero)
class Reader:
def __init__(self, options, args, skip_zero):
self.options = options
self.args = args
self.skip_zero = skip_zero
def __iter__(self):
return Options.Reader.Iterator(self)
class Iterator:
def __init__(self, reader):
self.options = reader.options
self.args = reader.args
self.optind = int(reader.skip_zero)
self.chrind = 1
self.endopt = False
def __next__(self):
if self.chrind == 0:
self.optind += 1
self.chrind = 1
if self.optind == len(self.args):
raise StopIteration
arg = self.args[self.optind]
if self.endopt or arg == '-' or not arg.startswith('-'):
self.endopt = self.options.posixly_correct()
name = None
value = arg
elif arg == '--':
self.chrind = 0
self.endopt = True
return next(self)
elif not arg.startswith('--'):
name = '-' + arg[self.chrind]
self.chrind += 1
if self.chrind < len(arg):
if not self.options.needs_arg(name):
return (name, None)
value = arg[self.chrind:]
else:
value = None
elif '=' in arg and arg.index('=') >= 3:
name = arg.split('=', 1)[0]
if not self.options.needs_arg(name):
raise Exception('option "%s" does not take an argument' % name)
value = arg[len(name) + 1:]
else:
name = arg
value = None
if value is None and int(self.options.needs_arg(name)) > 0:
self.optind += 1
if self.optind == len(self.args):
raise Exception('option "%s" requires an argument' % name)
value = self.args[self.optind]
self.chrind = 0
return (name, value)
# -- Main --
def start(program_name, options, params, main_program):
parsed = Params() if params is None else params
try:
if sys.hexversion < 0x3050000:
raise Exception('python 3.5.0 or later required')
if params is None:
return main_program(options.reader(sys.argv), lambda name: options.fallback(name, parsed))
nonopt = []
for [name, value] in options.reader(sys.argv):
if name is None:
nonopt.append(value)
else:
options.parse(name, value, parsed)
return main_program(nonopt, parsed)
except Exception as ex:
if parsed.excstk:
raise # loses the message information, but preserves the start() caller stack info
message = getattr(ex, 'message', str(ex))
sys.stderr.write('%s: %s\n' % (sys.argv[0] if sys.argv[0] else program_name, message))
sys.exit(1)
+210
View File
@@ -0,0 +1,210 @@
/*
Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const tty = require('tty');
const fs = require('fs');
// -- InputFileStream --
const BLOCK_SIZE = 4096;
class InputFileStream {
constructor(fileName, encoding = 'binary') {
if (fileName != null) {
this.fd = fs.openSync(fileName, 'r');
this.stName = fileName;
} else {
this.fd = process.stdin.fd;
this.stName = '<stdin>';
}
this.encoding = encoding;
this.unseek();
this.lines = [];
this.index = 0;
this.buffer = Buffer.alloc(BLOCK_SIZE);
this.remainder = '';
}
close() {
this.unseek();
fs.closeSync(this.fd);
}
fstat() {
return (this.fd === process.stdin.fd || tty.isatty(this.fd)) ? null : fs.fstatSync(this.fd);
}
location() {
let location = ' ';
if (this.eof) {
location = 'EOF: ';
} else if (this.lineNo > 0) {
location = `${this.lineNo}: `;
}
return `${this.stName}:${location}`;
}
_readBlock() {
for (;;) {
try {
return fs.readSync(this.fd, this.buffer, 0, BLOCK_SIZE);
} catch (e) {
if (e.code === 'EOF') {
return 0;
}
if (e.code !== 'EAGAIN') {
this.unseek();
throw e;
}
}
}
}
readLine() {
return this.readLines(line => line);
}
readLines(callback) {
let line;
do {
while (this.index < this.lines.length) {
this.lineNo++;
line = callback(this.lines[this.index++].trimRight());
if (line != null) {
return line;
}
}
var count = this._readBlock();
this.index = 0;
this.lines = (this.remainder + this.buffer.toString(this.encoding, 0, count)).split('\n');
this.remainder = this.lines.pop();
this.eof = false;
} while (count > 0);
if (this.remainder.length > 0) {
this.lineNo++;
line = callback(this.remainder.trimRight());
this.remainder = '';
} else {
this.eof = true;
line = null;
}
return line;
}
unseek() {
this.lineNo = 0;
this.eof = false;
}
}
// -- OutputFileStream --
class OutputFileStream {
constructor(fileName, encoding = 'binary') {
if (fileName != null) {
this.fd = fs.openSync(fileName, 'w');
this.stName = fileName;
} else {
this.fd = process.stdout.fd;
this.stName = '<stdout>';
}
if (encoding == null && tty.isatty(this.fd)) {
throw new Error(this.location() + 'binary output may not be send to a terminal');
}
this.encoding = (encoding == null ? 'binary' : encoding);
this.fbbuf = Buffer.alloc(4);
this.closeAttempt = false;
}
close() {
this.closeAttempt = true;
fs.closeSync(this.fd);
}
destroy() {
let errors = '';
if (this.fd !== process.stdout.fd) {
if (!this.closeAttempt) {
try {
fs.closeSync(this.fd);
} catch (e) {
errors += `\n${this.stName}: close: ${e.message}`;
}
}
try {
fs.unlinkSync(this.stName);
} catch (e) {
errors += `\n${this.stName}: unlink: ${e.message}`;
}
}
return errors;
}
location() {
return this.stName + ': ';
}
write(buffer) {
fs.writeSync(this.fd, buffer, 0, buffer.length);
}
write8(value) {
this.fbbuf.writeUInt8(value, 0);
fs.writeSync(this.fd, this.fbbuf, 0, 1);
}
write16(value) {
this.fbbuf.writeUInt16LE(value, 0);
fs.writeSync(this.fd, this.fbbuf, 0, 2);
}
write32(value) {
this.fbbuf.writeUInt32LE(value, 0);
fs.writeSync(this.fd, this.fbbuf, 0, 4);
}
writeLine(text) {
fs.writeSync(this.fd, text + '\n', null, this.encoding);
}
writeProp(name, value) {
this.writeLine((name + ' ' + value).trimRight());
}
writeZStr(bstr, numZeros) {
fs.writeSync(this.fd, bstr, null, 'binary');
this.write(Buffer.alloc(numZeros));
}
}
// -- Export --
module.exports = Object.freeze({
InputFileStream,
OutputFileStream
});
+176
View File
@@ -0,0 +1,176 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import codecs
import struct
import sys
import os
# -- InputFileStream --
class InputFileStream:
def __init__(self, file_name, encoding='binary'):
if file_name is not None:
self.file = open(file_name, 'r') if encoding is None else open(file_name, 'rb')
self.st_name = file_name
else:
self.file = sys.stdin if encoding is None else sys.stdin.buffer
self.st_name = '<stdin>'
if encoding not in [None, 'binary']:
self.file = codecs.getreader(encoding)(self.file)
self.line_no = 0
self.eof = False
def close(self):
self.unseek()
self.file.close()
def fstat(self):
return None if (self.file == sys.stdin.buffer or self.file.isatty()) else os.fstat(self.file.fileno())
def location(self):
return '%s:%s' % (self.st_name, 'EOF: ' if self.eof else '%d: ' % self.line_no if self.line_no > 0 else ' ')
def process(self, callback):
try:
result = callback(self)
self.close()
return result
except Exception as ex:
ex.message = self.location() + getattr(ex, 'message', str(ex))
raise
def read_line(self):
return self.read_lines(lambda line: line)
def read_lines(self, callback):
try:
for line in self.file:
self.line_no += 1
self.eof = False
line = callback(line.rstrip())
if line is not None:
return line
except OSError:
self.unseek()
raise
self.eof = True
return None
def unseek(self):
self.line_no = 0
self.eof = False
# -- OutputFileStream --
class OutputFileStream:
def __init__(self, file_name, encoding='binary'):
if file_name is not None:
self.file = open(file_name, 'wb')
self.st_name = file_name
else:
self.file = sys.stdout.buffer
self.st_name = '<stdout>'
if encoding is None and self.file.isatty():
raise Exception(self.location() + 'binary output may not be send to a terminal')
self.encoding = (None if encoding == 'binary' else encoding)
self.close_attempt = False
def abort(self):
errors = ''
if self.file != sys.stdout.buffer:
if not self.close_attempt:
try:
self.close()
except Exception as ex:
errors += '\n%sclose: %s' % (self.location(), str(ex))
try:
os.remove(self.st_name)
except Exception as ex:
errors += '\n%sunlink: %s' % (self.location(), str(ex))
return errors
def close(self):
self.close_attempt = True
self.file.close()
def location(self):
return self.st_name + ': '
def process(self, callback):
try:
callback(self)
self.close()
except Exception as ex:
ex.message = self.location() + getattr(ex, 'message', str(ex)) + self.abort()
raise
def write(self, data):
self.file.write(data)
def write8(self, value):
self.write(struct.pack('B', value))
def write16(self, value):
self.write(struct.pack('<H', value))
def write32(self, value):
self.write(struct.pack('<L', value))
def write_line(self, text):
self.write((text if self.encoding is None else bytes(text, self.encoding)) + b'\n')
def write_prop(self, name, value):
self.write_line((bytes(name, 'ascii') + b' ' + value).rstrip())
def write_zstr(self, bstr, num_zeros):
self.write(bstr + bytes(num_zeros))
# -- read/write file --
def read_file(file_name, callback, encoding='binary'):
return InputFileStream(file_name, encoding).process(callback)
def write_file(file_name, callback, encoding='binary'):
return OutputFileStream(file_name, encoding).process(callback)
+130
View File
@@ -0,0 +1,130 @@
/*
Copyright (C) 2017-2019 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
// -- Various --
const UNICODE_MAX = 1114111; // 0x10FFFF
const UNICODE_BMP_MAX = 65535; // 0xFFFF
function parseDec(name, s, minValue = 0, maxValue = UNICODE_MAX) {
if (s.match(/^\s*-?\d+\s*$/) == null) {
throw new Error(`invalid ${name} format`);
}
const value = parseInt(s, 10);
if (minValue != null && value < minValue) {
throw new Error(`${name} must be >= ${minValue}`);
}
if (maxValue != null && value > maxValue) {
throw new Error(`${name} must be <= ${maxValue}`);
}
return value;
}
function parseHex(name, s, minValue = 0, maxValue = UNICODE_MAX) {
if (s.match(/^\s*(0[xX])?[\dA-Fa-f]+\s*$/) == null) {
throw new Error(`invalid ${name} format`);
}
const value = parseInt(s, 16);
if (minValue != null && value < minValue) {
throw new Error(`${name} must be >= ` + minValue.toString(16).toUpperCase());
}
if (maxValue != null && value > maxValue) {
throw new Error(`${name} must be <= ` + maxValue.toString(16).toUpperCase());
}
return value;
}
function unihex(code) {
return ('000' + code.toString(16).toUpperCase()).replace(/0+(?=[\dA-F]{4})/, '');
}
function round(value) {
const esround = Math.round(value);
return esround - Number(esround % 2 !== 0 && esround - value === 0.5);
}
function quote(s) {
return '"' + s.replace(/"/g, '""') + '"';
}
function unquote(s, name) {
if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
s = s.substring(1, s.length - 1).replace(/""/g, '"');
} else if (name != null) {
throw new Error(name + ' must be quoted');
}
return s;
}
function message(prefix, severity, text) {
process.stderr.write(`${prefix}${severity ? severity + ': ' : ''}${text}\n`);
}
function warning(prefix, text) {
message(prefix, 'warning', text);
}
function splitWords(name, value, count) {
const words = value.split(/\s+/, count + 1);
if (words.length !== count) {
throw new Error(`${name} must contain ${count} values`);
}
return words;
}
const GPL2PLUS_LICENSE = ('' +
'This program is free software; you can redistribute it and/or modify it\n' +
'under the terms of the GNU General Public License as published by the Free\n' +
'Software Foundation; either version 2 of the License, or (at your option)\n' +
'any later version.\n' +
'\n' +
'This program is distributed in the hope that it will be useful, but\n' +
'WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n' +
'or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n' +
'for more details.\n' +
'\n' +
'You should have received a copy of the GNU General Public License along\n' +
'with this program; if not, write to the Free Software Foundation, Inc.,\n' +
'51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n');
// -- Exports --
module.exports = Object.freeze({
UNICODE_MAX,
UNICODE_BMP_MAX,
parseDec,
parseHex,
unihex,
round,
quote,
unquote,
message,
warning,
splitWords,
GPL2PLUS_LICENSE
});
+98
View File
@@ -0,0 +1,98 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import sys
# -- Various --
UNICODE_MAX = 1114111 # 0x10FFFF
UNICODE_BMP_MAX = 65535 # 0xFFFF
def parse_dec(name, s, min_value=0, max_value=UNICODE_MAX):
try:
value = int(s)
except ValueError:
raise Exception('invalid %s format' % name)
if min_value is not None and value < min_value:
raise Exception('%s must be >= %d' % (name, min_value))
if max_value is not None and value > max_value:
raise Exception('%s must be <= %d' % (name, max_value))
return value
def parse_hex(name, s, min_value=0, max_value=UNICODE_MAX):
try:
value = int(s, 16)
except ValueError:
raise Exception('invalid %s format' % name)
if min_value is not None and value < min_value:
raise Exception('%s must be >= %X' % (name, min_value))
if max_value is not None and value > max_value:
raise Exception('%s must be <= %X' % (name, max_value))
return value
def quote(bstr):
return b'"%s"' % bstr.replace(b'"', b'""')
def unquote(bstr, name=None):
if len(bstr) >= 2 and bstr.startswith(b'"') and bstr.endswith(b'"'):
bstr = bstr[1 : len(bstr) - 1].replace(b'""', b'"')
elif name is not None:
raise Exception(name + ' must be quoted')
return bstr
def message(prefix, severity, text):
sys.stderr.write('%s%s%s\n' % (prefix, severity + ': ' if severity else '', text))
def warning(prefix, text):
message(prefix, 'warning', text)
def split_words(name, value, count):
words = value.split(None, count)
if len(words) != count:
raise Exception('%s must contain %d values' % (name, count))
return words
GPL2PLUS_LICENSE = ('' +
'This program is free software; you can redistribute it and/or modify it\n' +
'under the terms of the GNU General Public License as published by the Free\n' +
'Software Foundation; either version 2 of the License, or (at your option)\n' +
'any later version.\n' +
'\n' +
'This program is distributed in the hope that it will be useful, but\n' +
'WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n' +
'or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n' +
'for more details.\n' +
'\n' +
'You should have received a copy of the GNU General Public License along\n' +
'with this program; if not, write to the Free Software Foundation, Inc.,\n' +
'51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n')
+129
View File
@@ -0,0 +1,129 @@
/*
Copyright (C) 2018-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
const fncli = require('./fncli.js');
const fnio = require('./fnio.js');
const otb1exp = require('./otb1exp.js');
// -- Params --
class Params extends otb1exp.Params {
constructor() {
super();
this.output = null;
this.encoding = 'utf-8';
this.realTime = true;
}
}
// -- Options --
const HELP = ('' +
'usage: otb1cli [options] [INPUT]\n' +
'Convert a BDF font to OTB\n' +
'\n' +
' -o OUTPUT output file (default = stdout, may not be a terminal)\n' +
' -d DIR-HINT set font direction hint (default = 0)\n' +
' -e EM-SIZE set em size (default = 1024)\n' +
' -g LINE-GAP set line gap (default = 0)\n' +
' -l LOW-PPEM set lowest recorded PPEM (default = font height)\n' +
' -E ENCODING BDF string properties encoding (default = utf-8)\n' +
' -W WLANG-ID set Windows name-s language ID (default = 0x0409)\n' +
' -T use the current date and time for created/modified\n' +
' (default = get them from INPUT if not stdin/terminal)\n' +
' -X set xMaxExtent = 0 (default = max character width)\n' +
' -L write a single loca entry (default = CHARS entries)\n' +
' -P write PostScript glyph names (default = no names)\n' +
'\n' +
'Notes:\n' +
' The input must be a BDF 2.1 font with unicode encoding.\n' +
' All bitmaps are expanded first. Bitmap widths are used.\n' +
' Overlapping characters are not supported.\n');
const VERSION = 'otb1cli 0.22, Copyright (C) 2018-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE;
class Options extends otb1exp.Options {
constructor() {
super(['-o', '-E'], HELP, VERSION);
}
parse(name, value, params) {
switch (name) {
case '-o':
params.output = value;
break;
case '-E':
params.encoding = value;
break;
case '-T':
params.realTime = false;
break;
default:
super.parse(name, value, params);
}
}
}
// -- Main --
function mainProgram(nonopt, parsed) {
if (nonopt.length > 1) {
throw new Error('invalid number of arguments, try --help');
}
// READ INPUT
let ifs = new fnio.InputFileStream(nonopt[0], parsed.encoding);
try {
if (parsed.realTime) {
try {
const stat = ifs.fstat();
if (stat != null) {
parsed.created = stat.birthtime;
parsed.modified = stat.mtime;
}
} catch (e) {
fnutil.warning(ifs.location(), e.message);
}
}
var font = otb1exp.Font.read(ifs, parsed);
ifs.close();
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
// WRITE OUTPUT
let ofs = new fnio.OutputFileStream(parsed.output, null);
try {
const table = new otb1exp.SFNT(font);
ofs.write(table.data.slice(0, table.size));
ofs.close();
} catch (e) {
e.message = ofs.location() + e.message + ofs.destroy();
throw e;
}
}
if (require.main === module) {
fncli.start('otb1cli.js', new Options(), new Params(), mainProgram);
}
+99
View File
@@ -0,0 +1,99 @@
#
# Copyright (C) 2018-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from datetime import datetime, timezone
import fnutil
import fncli
import fnio
import otb1exp
# -- Params --
class Params(otb1exp.Params):
def __init__(self):
otb1exp.Params.__init__(self)
self.output_name = None
self.real_time = True
# -- Options --
HELP = ('' +
'usage: otb1cli [options] [INPUT]\n' +
'Convert a BDF font to OTB\n' +
'\n' +
' -o OUTPUT output file (default = stdout, may not be a terminal)\n' +
' -d DIR-HINT set font direction hint (default = 0)\n' +
' -e EM-SIZE set em size (default = 1024)\n' +
' -g LINE-GAP set line gap (default = 0)\n' +
' -l LOW-PPEM set lowest recorded PPEM (default = font height)\n' +
' -E ENCODING BDF string properties encoding (default = utf-8)\n' +
' -W WLANG-ID set Windows name-s language ID (default = 0x0409)\n' +
' -T use the current date and time for created/modified\n' +
' (default = get them from INPUT if not stdin/terminal)\n' +
' -X set xMaxExtent = 0 (default = max character width)\n' +
' -L write a single loca entry (default = CHARS entries)\n' +
' -P write PostScript glyph names (default = no names)\n' +
'\n' +
'Notes:\n' +
' The input must be a BDF 2.1 font with unicode encoding.\n' +
' All bitmaps are expanded first. Bitmap widths are used.\n' +
' Overlapping characters are not supported.\n')
VERSION = 'otb1cli 0.24, Copyright (C) 2018-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE
class Options(otb1exp.Options):
def __init__(self):
otb1exp.Options.__init__(self, ['-o'], HELP, VERSION)
def parse(self, name, value, params):
if name == '-o':
params.output_name = value
elif name == '-T':
params.real_time = False
else:
otb1exp.Options.parse(self, name, value, params)
# -- Main --
def main_program(nonopt, parsed):
if len(nonopt) > 1:
raise Exception('invalid number of arguments, try --help')
# READ INPUT
def read_otb(ifs):
if parsed.real_time:
try:
stat = ifs.fstat()
if stat:
parsed.created = datetime.fromtimestamp(stat.st_ctime, timezone.utc)
parsed.modified = datetime.fromtimestamp(stat.st_mtime, timezone.utc)
except Exception as ex:
fnutil.warning(ifs.location(), str(ex))
return otb1exp.Font.read(ifs, parsed)
font = fnio.read_file(nonopt[0] if nonopt else None, read_otb)
# WRITE OUTPUT
sfnt = otb1exp.SFNT(font)
fnio.write_file(parsed.output_name, lambda ofs: ofs.write(sfnt.data), encoding=None)
if __name__ == '__main__':
fncli.start('otb1cli.py', Options(), Params(), main_program)
+895
View File
@@ -0,0 +1,895 @@
/*
Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
const fncli = require('./fncli.js');
const bdf = require('./bdf.js');
const bdfexp = require('./bdfexp.js');
const otb1get = require('./otb1get.js');
// -- Table --
const TS_EMPTY = 0;
const TS_SMALL = 64;
const TS_LARGE = 1024;
class Table {
constructor(size, name) {
this.data = Buffer.alloc(size);
this.size = 0;
this.tableName = name;
}
checkSize(size) {
if (size !== this.size) {
throw new Error(`internal error: ${this.tableName} size = ${this.size} instead of ${size}`);
}
}
checksum() {
let cksum = 0;
for (let offset = 0; offset < this.size; offset += 4) {
cksum += this.data.readUInt32BE(offset);
}
return cksum >>> 0;
}
ensure(count) {
if (this.size + count > this.data.length) {
let newSize = this.data.length << 1;
while (this.size + count > newSize) {
newSize <<= 1;
}
const newData = Buffer.alloc(newSize);
this.data.copy(newData, 0, 0, this.size);
this.data = newData;
}
}
get padding() {
return ((this.size + 1) & 3) ^ 1;
}
rewriteUInt32(value, offset) {
this.data.writeUInt32BE(value, offset);
}
write(buffer) {
this.ensure(buffer.length);
buffer.copy(this.data, this.size);
this.size += buffer.length;
}
writeRC(size, writer, name) {
this.ensure(size);
try {
writer(this.size);
} catch (e) {
e.message = e.message.replace('"value"', `"${this.tableName}.${name}"`);
throw e;
}
this.size += size;
}
writeInt8(value, name) {
this.writeRC(1, (offset) => this.data.writeInt8(value, offset), name);
}
writeInt16(value, name) {
this.writeRC(2, (offset) => this.data.writeInt16BE(value, offset), name);
}
writeInt32(value, name) {
this.writeRC(4, (offset) => this.data.writeInt32BE(value, offset), name);
}
writeInt64(value, name) {
this.writeRC(8, (offset) => this.data.writeInt64BE(value, offset), name);
}
writeUInt8(value, name) {
this.writeRC(1, (offset) => this.data.writeUInt8(value, offset), name);
}
writeUInt16(value, name) {
this.writeRC(2, (offset) => this.data.writeUInt16BE(value, offset), name);
}
writeUInt32(value, name) {
this.writeRC(4, (offset) => this.data.writeUInt32BE(value, offset), name);
}
writeUInt48(value, name) {
this.writeUInt16(name, 0);
this.writeRC(6, (offset) => this.data.writeUIntBE(value, offset, 6), name);
}
writeFixed(value, name) {
this.writeRC(4, (offset) => this.data.writeInt32BE(fnutil.round(value * 65536), offset), name);
}
writeTable(table) {
this.write(table.data.slice(0, table.size));
}
}
// -- Params --
const EM_SIZE_MIN = 64;
const EM_SIZE_MAX = 16384;
const EM_SIZE_DEFAULT = 1024;
class Params extends fncli.Params {
constructor() {
super();
this.created = new Date();
this.modified = this.created;
this.dirHint = 0;
this.emSize = EM_SIZE_DEFAULT;
this.lineGap = 0;
this.lowPPem = 0;
this.wLangId = 0x0409;
this.xMaxExtent = true;
this.singleLoca = false;
this.postNames = false;
}
}
// -- Options --
class Options extends fncli.Options {
constructor(needArgs, helpText, versionText) {
super(needArgs.concat(['-d', '-e', '-g', '-l', '-W']), helpText, versionText);
}
parse(name, value, params) {
switch (name) {
case '-d':
params.dirHint = fnutil.parseDec('DIR-HINT', value, -2, 2);
break;
case '-e':
params.emSize = fnutil.parseDec('EM-SIZE', value, EM_SIZE_MIN, EM_SIZE_MAX);
break;
case '-g':
params.lineGap = fnutil.parseDec('LINE-GAP', value, 0, EM_SIZE_MAX << 1);
break;
case '-l':
params.lowPPem = fnutil.parseDec('LOW-PPEM', value, 1, bdf.DPARSE_LIMIT);
break;
case '-W':
params.wLangId = fnutil.parseHex('WLANG-ID', value, 0, 0x7FFF);
break;
case '-X':
params.xMaxExtent = false;
break;
case '-L':
params.singleLoca = true;
break;
case '-P':
params.postNames = true;
break;
default:
this.fallback(name, params);
}
}
}
// -- Font --
class Font extends bdfexp.Font {
constructor(params) {
super();
this.params = params;
this.emAscender = 0;
this.emDescender = 0;
this.emMaxWidth = 0;
this.macStyle = 0;
this.lineSize = 0;
}
get bmpOnly() {
return this.maxCode <= fnutil.UNICODE_BMP_MAX;
}
get created() {
return Font.sfntime(this.params.created);
}
emScale(value, divisor) {
return fnutil.round(value * this.params.emSize / (divisor || this.bbx.height));
}
get italicAngle() {
const value = this.props.get('ITALIC_ANGLE'); // must be integer
return value != null ? fnutil.parseDec('ITALIC_ANGLE', value, -45, 45) : this.italic ? -11.5 : 0;
}
get maxCode() {
return this.chars.slice(-1)[0].code;
}
get minCode() {
return this.chars[0].code;
}
get modified() {
return Font.sfntime(this.params.modified);
}
prepare() {
this.chars.sort((c1, c2) => c1.code - c2.code);
this.chars = this.chars.filter((c, index, array) => index === 0 || c.code !== array[index - 1].code);
this.props.set('CHARS', this.chars.length);
this.emAscender = this.emScale(this.pxAscender);
this.emDescender = this.emAscender - this.params.emSize;
this.emMaxWidth = this.emScaleWidth(this);
this.macStyle = Number(this.bold) + (Number(this.italic) << 1);
this.lineSize = this.emScale(fnutil.round(this.bbx.height / 17) || 1);
}
_read(input) {
super._read(input);
this.prepare();
return this;
}
static read(input, params) {
return (new Font(params))._read(input);
}
emScaleWidth(base) {
return this.emScale(base.bbx.width);
}
static sfntime(stamp) {
return Math.floor((stamp - Date.UTC(1904, 0, 1)) / 1000);
}
get underlinePosition() {
return fnutil.round((this.emDescender + this.lineSize) / 2);
}
get xMaxExtent() {
return this.params.xMaxExtent ? this.emMaxWidth : 0;
}
}
// -- BDAT --
const BDAT_HEADER_SIZE = 4;
const BDAT_METRIC_SIZE = 5;
class BDAT extends Table {
constructor(font) {
super(TS_LARGE, 'EBDT');
// header
this.writeFixed(2, 'version');
// format 1 data
font.chars.forEach(char => {
this.writeUInt8(font.bbx.height, 'height');
this.writeUInt8(char.bbx.width, 'width');
this.writeInt8(0, 'bearingX');
this.writeInt8(font.pxAscender, 'bearingY');
this.writeUInt8(char.bbx.width, 'advance');
this.write(char.data); // imageData
});
}
static getCharSize(char) {
return BDAT_METRIC_SIZE + char.data.length;
}
}
// -- BLOC --
const BLOC_TABLE_SIZE_OFFSET = 12;
const BLOC_PREFIX_SIZE = 0x38; // header 0x08 + 1 bitmapSizeTable * 0x30
const BLOC_INDEX_ARRAY_SIZE = 8; // 1 index record * 0x08
class BLOC extends Table {
constructor(font) {
super(TS_SMALL, 'EBLC');
// header
this.writeFixed(2, 'version');
this.writeUInt32(1, 'numSizes');
// bitmapSizeTable
this.writeUInt32(BLOC_PREFIX_SIZE, 'indexSubTableArrayOffset');
this.writeUInt32(0, 'indexTableSize'); // adjusted later
this.writeUInt32(1, 'numberOfIndexSubTables');
this.writeUInt32(0, 'colorRef');
// hori
this.writeInt8(font.pxAscender, 'hori ascender');
this.writeInt8(font.pxDescender, 'hori descender');
this.writeUInt8(font.bbx.width, 'hori widthMax');
this.writeInt8(1, 'hori caretSlopeNumerator');
this.writeInt8(0, 'hori caretSlopeDenominator');
this.writeInt8(0, 'hori caretOffset');
this.writeInt8(0, 'hori minOriginSB');
this.writeInt8(0, 'hori minAdvanceSB');
this.writeInt8(font.pxAscender, 'hori maxBeforeBL');
this.writeInt8(font.pxDescender, 'hori minAfterBL');
this.writeInt16(0, 'hori padd');
// vert
this.writeInt8(0, 'vert ascender');
this.writeInt8(0, 'vert descender');
this.writeUInt8(0, 'vert widthMax');
this.writeInt8(0, 'vert caretSlopeNumerator');
this.writeInt8(0, 'vert caretSlopeDenominator');
this.writeInt8(0, 'vert caretOffset');
this.writeInt8(0, 'vert minOriginSB');
this.writeInt8(0, 'vert minAdvanceSB');
this.writeInt8(0, 'vert maxBeforeBL');
this.writeInt8(0, 'vert minAfterBL');
this.writeInt16(0, 'vert padd');
// (bitmapSizeTable)
this.writeUInt16(0, 'startGlyphIndex');
this.writeUInt16(font.chars.length - 1, 'endGlyphIndex');
this.writeUInt8(font.bbx.height, 'ppemX');
this.writeUInt8(font.bbx.height, 'ppemY');
this.writeUInt8(1, 'bitDepth');
this.writeUInt8(1, 'flags'); // small metrics are horizontal
// indexSubTableArray
this.writeUInt16(0, 'firstGlyphIndex');
this.writeUInt16(font.chars.length - 1, 'lastGlyphIndex');
this.writeUInt32(BLOC_INDEX_ARRAY_SIZE, 'additionalOffsetToIndexSubtable');
// indexSubtableHeader
this.writeUInt16(font.proportional ? 1 : 2, 'indexFormat');
this.writeUInt16(1, 'imageFormat'); // BDAT -> small metrics, byte-aligned
this.writeUInt32(BDAT_HEADER_SIZE, 'imageDataOffset');
// indexSubtable data
if (font.proportional) {
let offset = 0;
font.chars.forEach(char => {
this.writeUInt32(offset, 'offsetArray[]');
offset += BDAT.getCharSize(char);
});
this.writeUInt32(offset, 'offsetArray[]');
} else {
this.writeUInt32(BDAT.getCharSize(font.chars[0]), 'imageSize');
this.writeUInt8(font.bbx.height, 'height');
this.writeUInt8(font.bbx.width, 'width');
this.writeInt8(0, 'horiBearingX');
this.writeInt8(font.pxAscender, 'horiBearingY');
this.writeUInt8(font.bbx.width, 'horiAdvance');
this.writeInt8(-(font.bbx.width >> 1), 'vertBearingX');
this.writeInt8(0, 'vertBearingY');
this.writeUInt8(font.bbx.height, 'vertAdvance');
}
// adjust
this.rewriteUInt32(this.size - BLOC_PREFIX_SIZE, BLOC_TABLE_SIZE_OFFSET);
}
}
// -- OS/2 --
const OS_2_TABLE_SIZE = 96;
class OS_2 extends Table {
constructor(font) {
super(TS_SMALL, 'OS/2');
// Version 4
const xAvgCharWidth = font.emScale(font.avgWidth); // otb1get.xAvgCharWidth(font);
const ulCharRanges = otb1get.ulCharRanges(font);
const ulCodePages = font.bmpOnly ? otb1get.ulCodePages(font) : [0, 0];
// mostly from FontForge
const scriptXSize = font.emScale(30, 100);
const scriptYSize = font.emScale(40, 100);
const subscriptYOff = scriptYSize >> 1;
const xfactor = Math.tan(font.italicAngle * Math.PI / 180);
const subscriptXOff = 0; // stub, no overlapping characters yet
const superscriptYOff = font.emAscender - scriptYSize;
const superscriptXOff = -fnutil.round(xfactor * superscriptYOff);
// write
this.writeUInt16(4, 'version');
this.writeInt16(xAvgCharWidth, 'xAvgCharWidth');
this.writeUInt16(font.bold ? 700 : 400, 'usWeightClass');
this.writeUInt16(5, 'usWidthClass'); // medium
this.writeInt16(0, 'fsType');
this.writeInt16(scriptXSize, 'ySubscriptXSize');
this.writeInt16(scriptYSize, 'ySubscriptYSize');
this.writeInt16(subscriptXOff, 'ySubscriptXOffset');
this.writeInt16(subscriptYOff, 'ySubscriptYOffset');
this.writeInt16(scriptXSize, 'ySuperscriptXSize');
this.writeInt16(scriptYSize, 'ySuperscriptYSize');
this.writeInt16(superscriptXOff, 'ySuperscriptXOffset');
this.writeInt16(superscriptYOff, 'ySuperscriptYOffset');
this.writeInt16(font.lineSize, 'yStrikeoutSize');
this.writeInt16(font.emScale(25, 100), 'yStrikeoutPosition');
this.writeInt16(0, 'sFamilyClass'); // no classification
this.writeUInt8(2, 'bFamilyType'); // text and display
this.writeUInt8(0, 'bSerifStyle'); // any
this.writeUInt8(font.bold ? 8 : 6, 'bWeight');
this.writeUInt8(font.proportional ? 3 : 9, 'bProportion');
this.writeUInt8(0, 'bContrast');
this.writeUInt8(0, 'bStrokeVariation');
this.writeUInt8(0, 'bArmStyle');
this.writeUInt8(0, 'bLetterform');
this.writeUInt8(0, 'bMidline');
this.writeUInt8(0, 'bXHeight');
this.writeUInt32(ulCharRanges[0], 'ulCharRange1');
this.writeUInt32(ulCharRanges[1], 'ulCharRange2');
this.writeUInt32(ulCharRanges[2], 'ulCharRange3');
this.writeUInt32(ulCharRanges[3], 'ulCharRange4');
this.writeUInt32(0x586F7334, 'achVendID'); // 'Xos4'
this.writeUInt16(OS_2.fsSelection(font), 'fsSelection');
this.writeUInt16(Math.min(font.minCode, fnutil.UNICODE_BMP_MAX), 'firstChar');
this.writeUInt16(Math.min(font.maxCode, fnutil.UNICODE_BMP_MAX), 'lastChar');
this.writeInt16(font.emAscender, 'sTypoAscender');
this.writeInt16(font.emDescender, 'sTypoDescender');
this.writeInt16(font.params.lineGap, 'sTypoLineGap');
this.writeUInt16(font.emAscender, 'usWinAscent');
this.writeUInt16(-font.emDescender, 'usWinDescent');
this.writeUInt32(ulCodePages[0], 'ulCodePageRange1');
this.writeUInt32(ulCodePages[1], 'ulCodePageRange2');
this.writeInt16(font.emScale(font.pxAscender * 0.6), 'sxHeight'); // stub
this.writeInt16(font.emScale(font.pxAscender * 0.8), 'sCapHeight'); // stub
this.writeUInt16(OS_2.defaultChar(font), 'usDefaultChar');
this.writeUInt16(OS_2.breakChar(font), 'usBreakChar');
this.writeUInt16(1, 'usMaxContext');
// check
this.checkSize(OS_2_TABLE_SIZE);
}
static breakChar(font) {
return font.chars.findIndex(char => char.code === 0x20) !== -1 ? 0x20 : font.minCode;
}
static defaultChar(font) {
if (font.defaultCode !== -1 && font.defaultCode <= fnutil.UNICODE_BMP_MAX) {
return font.defaultCode;
}
return font.minCode && font.maxCode;
}
static fsSelection(font) {
const fsSelection = Number(font.bold) * 5 + Number(font.italic);
return fsSelection || (font.xlfd[bdf.XLFD.SLANT] === 'R' ? 0x40 : 0);
}
}
// -- cmap --
const CMAP_4_PREFIX_SIZE = 12;
const CMAP_4_FORMAT_SIZE = 16;
const CMAP_4_SEGMENT_SIZE = 8;
const CMAP_12_PREFIX_SIZE = 20;
const CMAP_12_FORMAT_SIZE = 16;
const CMAP_12_GROUP_SIZE = 12;
class CMapRange {
constructor(glyphIndex = 0, startCode = 0, finalCode = -2) {
this.glyphIndex = glyphIndex;
this.startCode = startCode;
this.finalCode = finalCode;
}
get idDelta() {
return (this.glyphIndex - this.startCode) & 0xFFFF;
}
}
class CMAP extends Table {
constructor(font) {
super(TS_LARGE, 'cmap');
// make ranges
let ranges = [];
let range = new CMapRange();
for (let index = 0; index < font.chars.length; index++) {
let code = font.chars[index].code;
if (code === range.finalCode + 1) {
range.finalCode++;
} else {
range = new CMapRange(index, code, code);
ranges.push(range);
}
}
// write
if (font.bmpOnly) {
if (font.maxCode < 0xFFFF) {
ranges.push(new CMapRange(0, 0xFFFF, 0xFFFF));
}
this.writeFormat4(ranges);
} else {
this.writeFormat12(ranges);
}
}
writeFormat4(ranges) {
// index
this.writeUInt16(0, 'version');
this.writeUInt16(1, 'numberSubtables');
// encoding subtables index
this.writeUInt16(3, 'platformID'); // Microsoft
this.writeUInt16(1, 'platformSpecificID'); // Unicode BMP (UCS-2)
this.writeUInt32(CMAP_4_PREFIX_SIZE, 'offset'); // for Unicode BMP (UCS-2)
// cmap format 4
const segCount = ranges.length;
const subtableSize = CMAP_4_FORMAT_SIZE + segCount * CMAP_4_SEGMENT_SIZE;
const searchRange = 2 << Math.floor(Math.log2(segCount));
this.writeUInt16(4, 'format');
this.writeUInt16(subtableSize, 'length');
this.writeUInt16(0, 'language'); // none/independent
this.writeUInt16(segCount * 2, 'segCountX2');
this.writeUInt16(searchRange, 'searchRange');
this.writeUInt16(Math.log2(searchRange / 2), 'entrySelector');
this.writeUInt16((segCount * 2) - searchRange, 'rangeShift');
ranges.forEach(range => {
this.writeUInt16(range.finalCode, 'endCode');
});
this.writeUInt16(0, 'reservedPad');
ranges.forEach(range => {
this.writeUInt16(range.startCode, 'startCode');
});
ranges.forEach(range => {
this.writeUInt16(range.idDelta, 'idDelta');
});
ranges.forEach(() => this.writeUInt16(0), 'idRangeOffset');
// check
this.checkSize(CMAP_4_PREFIX_SIZE + subtableSize);
}
writeFormat12(ranges) {
// index
this.writeUInt16(0, 'version');
this.writeUInt16(2, 'numberSubtables');
// encoding subtables
this.writeUInt16(0, 'platformID'); // Unicode
this.writeUInt16(4, 'platformSpecificID'); // Unicode 2.0+ full range
this.writeUInt32(CMAP_12_PREFIX_SIZE, 'offset'); // for Unicode 2.0+ full range
this.writeUInt16(3, 'platformID'); // Microsoft
this.writeUInt16(10, 'platformSpecificID'); // Unicode UCS-4
this.writeUInt32(CMAP_12_PREFIX_SIZE, 'offset'); // for Unicode UCS-4
// cmap format 12
const subtableSize = CMAP_12_FORMAT_SIZE + ranges.length * CMAP_12_GROUP_SIZE;
this.writeFixed(12, 'format');
this.writeUInt32(subtableSize, 'length');
this.writeUInt32(0, 'language'); // none/independent
this.writeUInt32(ranges.length, 'nGroups');
this.ranges.forEach(range => {
this.writeUInt32(range.startCode, 'startCharCode');
this.writeUInt32(range.finalCode, 'endCharCode');
this.writeUInt32(range.glyphIndex, 'startGlyphID');
});
// check
this.checkSize(CMAP_12_PREFIX_SIZE + subtableSize);
}
}
// -- glyf --
class GLYF extends Table {
constructor() {
super(TS_EMPTY, 'glyf');
}
}
// -- head --
const HEAD_TABLE_SIZE = 54;
const HEAD_CHECKSUM_OFFSET = 8;
class HEAD extends Table {
constructor(font) {
super(TS_SMALL, 'head');
this.writeFixed(1, 'version');
this.writeFixed(1, 'fontRevision');
this.writeUInt32(0, 'checksumAdjustment'); // adjusted later
this.writeUInt32(0x5F0F3CF5, 'magicNumber');
this.writeUInt16(HEAD.flags(font), 'flags');
this.writeUInt16(font.params.emSize, 'unitsPerEm');
this.writeUInt48(font.created, 'created');
this.writeUInt48(font.modified, 'modified');
this.writeInt16(0, 'xMin');
this.writeInt16(font.emDescender, 'yMin');
this.writeInt16(font.emMaxWidth, 'xMax');
this.writeInt16(font.emAscender, 'yMax');
this.writeUInt16(font.macStyle, 'macStyle');
this.writeUInt16(font.params.lowPPem || font.bbx.height, 'lowestRecPPEM');
this.writeInt16(font.params.dirHint, 'fontDirectionHint');
this.writeInt16(0, 'indexToLocFormat'); // short
this.writeInt16(0, 'glyphDataFormat'); // current
// check
this.checkSize(HEAD_TABLE_SIZE);
}
static flags(font) {
return otb1get.containsRTL(font) ? 0x020B : 0x0B; // y0 base, x0 lsb, scale int
}
}
// -- hhea --
const HHEA_TABLE_SIZE = 36;
class HHEA extends Table {
constructor(font) {
super(TS_SMALL, 'hhea');
this.writeFixed(1, 'version');
this.writeInt16(font.emAscender, 'ascender');
this.writeInt16(font.emDescender, 'descender');
this.writeInt16(font.params.lineGap, 'lineGap');
this.writeUInt16(font.emMaxWidth, 'advanceWidthMax');
this.writeInt16(0, 'minLeftSideBearing');
this.writeInt16(0, 'minRightSideBearing');
this.writeInt16(font.xMaxExtent, 'xMaxExtent');
this.writeInt16(font.italic ? 100 : 1, 'caretSlopeRise');
this.writeInt16(font.italic ? 20 : 0, 'caretSlopeRun');
this.writeInt16(0, 'caretOffset');
this.writeInt16(0, 'reserved');
this.writeInt16(0, 'reserved');
this.writeInt16(0, 'reserved');
this.writeInt16(0, 'reserved');
this.writeInt16(0, 'metricDataFormat'); // current
this.writeUInt16(font.chars.length, 'numOfLongHorMetrics');
// check
this.checkSize(HHEA_TABLE_SIZE);
}
}
// -- hmtx --
class HMTX extends Table {
constructor(font) {
super(TS_LARGE, 'hmtx');
font.chars.forEach(char => {
this.writeUInt16(font.emScaleWidth(char), 'advanceWidth');
this.writeInt16(0, 'leftSideBearing');
});
}
}
// -- loca --
class LOCA extends Table {
constructor(font) {
super(TS_SMALL, 'loca');
if (!font.params.singleLoca) {
font.chars.forEach(() => this.writeUInt16(0, 'offset'));
}
this.writeUInt16(0, 'offset');
}
}
// -- maxp --
const MAXP_TABLE_SIZE = 32;
class MAXP extends Table {
constructor(font) {
super(TS_SMALL, 'maxp');
this.writeFixed(1, 'version');
this.writeUInt16(font.chars.length, 'numGlyphs');
this.writeUInt16(0, 'maxPoints');
this.writeUInt16(0, 'maxContours');
this.writeUInt16(0, 'maxComponentPoints');
this.writeUInt16(0, 'maxComponentContours');
this.writeUInt16(2, 'maxZones');
this.writeUInt16(0, 'maxTwilightPoints');
this.writeUInt16(1, 'maxStorage');
this.writeUInt16(1, 'maxFunctionDefs');
this.writeUInt16(0, 'maxInstructionDefs');
this.writeUInt16(64, 'maxStackElements');
this.writeUInt16(0, 'maxSizeOfInstructions');
this.writeUInt16(0, 'maxComponentElements');
this.writeUInt16(0, 'maxComponentDepth');
// check
this.checkSize(MAXP_TABLE_SIZE);
}
}
// -- name --
const NAME_ID = {
COPYRIGHT: 0,
FONT_FAMILY: 1,
FONT_SUBFAMILY: 2,
UNIQUE_SUBFAMILY: 3,
FULL_FONT_NAME: 4,
LICENSE: 14
};
const NAME_HEADER_SIZE = 6;
const NAME_RECORD_SIZE = 12;
class NAME extends Table {
constructor(font) {
super(TS_LARGE, 'name');
// compute names
let names = new Map();
const copyright = font.props.get('COPYRIGHT');
if (copyright != null) {
names.set(NAME_ID.COPYRIGHT, fnutil.unquote(copyright));
}
const family = font.xlfd[bdf.XLFD.FAMILY_NAME];
const style = ['Regular', 'Bold', 'Italic', 'Bold Italic'][font.macStyle];
names.set(NAME_ID.FONT_FAMILY, family);
names.set(NAME_ID.FONT_SUBFAMILY, style);
names.set(NAME_ID.UNIQUE_SUBFAMILY, `${family} ${style} bitmap height ${font.bbx.height}`);
names.set(NAME_ID.FULL_FONT_NAME, `${family} ${style}`);
let license = font.props.get('LICENSE');
const notice = font.props.get('NOTICE');
if (license == null && notice != null && notice.toLowerCase().includes('license')) {
license = notice;
}
if (license != null) {
names.set(NAME_ID.LICENSE, fnutil.unquote(license));
}
// header
const count = names.size * (1 + 1); // Unicode + Microsoft
const stringOffset = NAME_HEADER_SIZE + NAME_RECORD_SIZE * count;
this.writeUInt16(0, 'format');
this.writeUInt16(count, 'count');
this.writeUInt16(stringOffset, 'stringOffset');
// name records / create values
let values = new Table(TS_LARGE, 'name');
names.forEach((str, nameID) => {
const value = Buffer.from(str, 'utf16le').swap16();
const bmp = font.bmpOnly && value.length === str.length * 2;
// Unicode
this.writeUInt16(0, 'platformID'); // Unicode
this.writeUInt16(bmp ? 3 : 4, 'platformSpecificID');
this.writeUInt16(0, 'languageID');
this.writeUInt16(nameID, 'nameID');
this.writeUInt16(value.length, 'length'); // in bytes
this.writeUInt16(values.size, 'offset');
// Windows
this.writeUInt16(3, 'platformID'); // Microsoft
this.writeUInt16(bmp ? 1 : 10, 'platformSpecificID');
this.writeUInt16(font.params.wLangId, 'languageID');
this.writeUInt16(nameID, 'nameID');
this.writeUInt16(value.length, 'length'); // in bytes
this.writeUInt16(values.size, 'offset');
// value
values.write(value);
});
// write values
this.writeTable(values);
// check
this.checkSize(stringOffset + values.size);
}
}
// -- post --
const POST_TABLE_SIZE = 32;
class POST extends Table {
constructor(font) {
super(TS_SMALL, 'post');
this.writeFixed(font.params.postNames ? 2 : 3, 'format');
this.writeFixed(font.italicAngle, 'italicAngle');
this.writeInt16(font.underlinePosition, 'underlinePosition');
this.writeInt16(font.lineSize, 'underlineThickness');
this.writeUInt32(font.proportional ? 0 : 1, 'isFixedPitch');
this.writeUInt32(0, 'minMemType42');
this.writeUInt32(0, 'maxMemType42');
this.writeUInt32(0, 'minMemType1');
this.writeUInt32(0, 'maxMemType1');
// names
if (font.params.postNames) {
let postNames = otb1get.postMacNames();
const postMacCount = postNames.length;
this.writeUInt16(font.chars.length, 'numberOfGlyphs');
font.chars.forEach(char => {
const name = char.props.get('STARTCHAR');
const index = postNames.indexOf(name);
if (index !== -1) {
this.writeUInt16(index, 'glyphNameIndex');
} else {
this.writeUInt16(postNames.length, 'glyphNameIndex');
postNames.push(name);
}
});
postNames.slice(postMacCount).forEach(name => {
this.writeUInt8(name.length, 'glyphNameLength');
this.write(Buffer.from(name, 'binary'));
});
// check
} else {
this.checkSize(POST_TABLE_SIZE);
}
}
}
// -- SFNT --
const SFNT_HEADER_SIZE = 12;
const SFNT_RECORD_SIZE = 16;
const SFNT_SUBTABLES = [ BDAT, BLOC, OS_2, CMAP, GLYF, HEAD, HHEA, HMTX, LOCA, MAXP, NAME, POST ];
class SFNT extends Table {
constructor(font) {
super(TS_LARGE, 'SFNT');
// create tables
let tables = [];
SFNT_SUBTABLES.forEach(Ctor => {
tables.push(new Ctor(font));
});
// header
const numTables = tables.length;
const entrySelector = Math.floor(Math.log2(numTables));
const searchRange = 16 << entrySelector;
const contentOffset = SFNT_HEADER_SIZE + numTables * SFNT_RECORD_SIZE;
let offset = contentOffset;
let content = new Table(TS_LARGE, 'SFNT');
let headChecksumOffset = -1;
this.writeFixed(1, 'sfntVersion');
this.writeUInt16(numTables, 'numTables');
this.writeUInt16(searchRange, 'searchRange');
this.writeUInt16(entrySelector, 'entrySelector');
this.writeUInt16(numTables * 16 - searchRange, 'rangeShift');
// table records / create content
tables.forEach(table => {
this.write(Buffer.from(table.tableName, 'binary'));
this.writeUInt32(table.checksum(), 'checkSum');
this.writeUInt32(offset, 'offset');
this.writeUInt32(table.size, 'length');
// create content
if (table.tableName === 'head') {
headChecksumOffset = offset + HEAD_CHECKSUM_OFFSET;
}
const paddedSize = table.size + table.padding;
content.write(table.data.slice(0, paddedSize));
offset += paddedSize;
});
// write content
this.writeTable(content);
// check
this.checkSize(contentOffset + content.size);
// adjust
if (headChecksumOffset !== -1) {
this.rewriteUInt32((0xB1B0AFBA - this.checksum()) >>> 0, headChecksumOffset);
}
}
}
// -- Export --
module.exports = Object.freeze({
TS_EMPTY,
TS_SMALL,
TS_LARGE,
Table,
EM_SIZE_MIN,
EM_SIZE_MAX,
EM_SIZE_DEFAULT,
Params,
Options,
Font,
BDAT,
BLOC,
OS_2,
CMAP,
GLYF,
HEAD,
HHEA,
HMTX,
LOCA,
MAXP,
NAME,
POST,
SFNT
});
+808
View File
@@ -0,0 +1,808 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import struct
import codecs
import math
from datetime import datetime, timezone
from itertools import groupby
from enum import IntEnum, unique
from collections import OrderedDict
import fnutil
import fncli
import bdf
import bdfexp
import otb1get
# -- Table --
class Table:
def __init__(self, name):
self.data = bytearray(0)
self.table_name = name
def check_size(self, size):
if size != self.size:
raise Exception('internal error: %s size = %d instead of %d' % (self.table_name, self.size, size))
def checksum(self):
cksum = 0
data = self.data + self.padding
for offset in range(0, self.size, 4):
cksum += struct.unpack('>L', data[offset : offset + 4])[0]
return cksum & 0xFFFFFFFF
def pack(self, format, value, name):
try:
return struct.pack(format, value)
except struct.error as ex:
raise Exception('%s.%s: %s' % (self.table_name, name, str(ex)))
@property
def size(self):
return len(self.data)
@property
def padding(self):
return bytes(((self.size + 1) & 3) ^ 1)
def rewrite_uint32(self, value, offset):
self.data[offset : offset + 4] = struct.pack('>L', value)
def write(self, data):
self.data += data
def write_int8(self, value, name):
self.data += self.pack('b', value, name)
def write_uint8(self, value, name):
self.data += self.pack('B', value, name)
def write_int16(self, value, name):
self.data += self.pack('>h', value, name)
def write_uint16(self, value, name):
self.data += self.pack('>H', value, name)
def write_uint32(self, value, name):
self.data += self.pack('>L', value, name)
def write_uint64(self, value, name):
self.data += self.pack('>Q', value, name)
def write_fixed(self, value, name):
self.data += self.pack('>l', round(value * 65536), name)
def write_table(self, table):
self.data += table.data
# -- Params --
EM_SIZE_MIN = 64
EM_SIZE_MAX = 16384
EM_SIZE_DEFAULT = 1024
class Params(fncli.Params): # pylint: disable=too-many-instance-attributes
def __init__(self):
fncli.Params.__init__(self)
self.created = datetime.now(timezone.utc)
self.modified = self.created
self.dir_hint = 0
self.em_size = EM_SIZE_DEFAULT
self.line_gap = 0
self.low_ppem = 0
self.encoding = 'utf_8'
self.w_lang_id = 0x0409
self.x_max_extent = True
self.single_loca = False
self.post_names = False
# -- Options --
class Options(fncli.Options):
def __init__(self, need_args, help_text, version_text):
fncli.Options.__init__(self, need_args + ['-d', '-e', '-g', '-l', '-E', '-W'], help_text, version_text)
def parse(self, name, value, params):
if name == '-d':
params.dir_hint = fnutil.parse_dec('DIR-HINT', value, -2, 2)
elif name == '-e':
params.em_size = fnutil.parse_dec('EM-SIZE', value, EM_SIZE_MIN, EM_SIZE_MAX)
elif name == '-g':
params.line_gap = fnutil.parse_dec('LINE-GAP', value, 0, EM_SIZE_MAX << 1)
elif name == '-l':
params.low_ppem = fnutil.parse_dec('LOW-PPEM', value, 1, bdf.DPARSE_LIMIT)
elif name == '-E':
params.encoding = value
elif name == '-W':
params.w_lang_id = fnutil.parse_hex('WLANG-ID', value, 0, 0x7FFF)
elif name == '-X':
params.x_max_extent = False
elif name == '-L':
params.single_loca = True
elif name == '-P':
params.post_names = True
else:
self.fallback(name, params)
# -- Font --
class Font(bdfexp.Font):
def __init__(self, params):
bdfexp.Font.__init__(self)
self.params = params
self.em_ascender = 0
self.em_descender = 0
self.em_max_width = 0
self.mac_style = 0
self.line_size = 0
@property
def bmp_only(self):
return self.max_code <= fnutil.UNICODE_BMP_MAX
@property
def created(self):
return Font.sfntime(self.params.created)
def decode(self, data):
return codecs.decode(data, self.params.encoding)
def em_scale(self, value, divisor=0):
return round(value * self.params.em_size / (divisor or self.bbx.height))
def em_scale_width(self, base):
return self.em_scale(base.bbx.width)
@property
def italic_angle(self):
value = self.props.get('ITALIC_ANGLE') # must be integer
return fnutil.parse_dec('ITALIC_ANGLE', value, -45, 45) if value else -11.5 if self.italic else 0
@property
def max_code(self):
return self.chars[-1].code
@property
def min_code(self):
return self.chars[0].code
@property
def modified(self):
return Font.sfntime(self.params.modified)
def prepare(self):
self.chars.sort(key=lambda c: c.code)
self.chars = [next(elem[1]) for elem in groupby(self.chars, key=lambda c: c.code)]
self.props.set('CHARS', len(self.chars))
self.em_ascender = self.em_scale(self.px_ascender)
self.em_descender = self.em_ascender - self.params.em_size
self.em_max_width = self.em_scale_width(self)
self.mac_style = int(self.bold) + (int(self.italic) << 1)
self.line_size = self.em_scale(round(self.bbx.height / 17) or 1)
def _read(self, input):
bdfexp.Font._read(self, input)
self.prepare()
return self
@staticmethod
def read(input, params): # pylint: disable=arguments-differ
return Font(params)._read(input) # pylint: disable=protected-access
@staticmethod
def sfntime(stamp):
return math.floor((stamp - datetime(1904, 1, 1, tzinfo=timezone.utc)).total_seconds())
@property
def underline_position(self):
return round((self.em_descender + self.line_size) / 2)
@property
def x_max_extent(self):
return self.em_max_width if self.params.x_max_extent else 0
# -- BDAT --
BDAT_HEADER_SIZE = 4
BDAT_METRIC_SIZE = 5
class BDAT(Table):
def __init__(self, font):
Table.__init__(self, 'EBDT')
# header
self.write_fixed(2, 'version')
# format 1 data
for char in font.chars:
self.write_uint8(font.bbx.height, 'height')
self.write_uint8(char.bbx.width, 'width')
self.write_int8(0, 'bearingX')
self.write_int8(font.px_ascender, 'bearingY')
self.write_uint8(char.bbx.width, 'advance')
self.write(char.data) # imageData
@staticmethod
def get_char_size(char):
return BDAT_METRIC_SIZE + len(char.data)
# -- BLOC --
BLOC_TABLE_SIZE_OFFSET = 12
BLOC_PREFIX_SIZE = 0x38 # header 0x08 + 1 bitmapSizeTable * 0x30
BLOC_INDEX_ARRAY_SIZE = 8 # 1 index record * 0x08
class BLOC(Table):
def __init__(self, font):
Table.__init__(self, 'EBLC')
# header
self.write_fixed(2, 'version')
self.write_uint32(1, 'numSizes')
# bitmapSizeTable
self.write_uint32(BLOC_PREFIX_SIZE, 'indexSubTableArrayOffset')
self.write_uint32(0, 'indexTableSize') # adjusted later
self.write_uint32(1, 'numberOfIndexSubTables')
self.write_uint32(0, 'colorRef')
# hori
self.write_int8(font.px_ascender, 'hori ascender')
self.write_int8(font.px_descender, 'hori descender')
self.write_uint8(font.bbx.width, 'hori widthMax')
self.write_int8(1, 'hori caretSlopeNumerator')
self.write_int8(0, 'hori caretSlopeDenominator')
self.write_int8(0, 'hori caretOffset')
self.write_int8(0, 'hori minOriginSB')
self.write_int8(0, 'hori minAdvanceSB')
self.write_int8(font.px_ascender, 'hori maxBeforeBL')
self.write_int8(font.px_descender, 'hori minAfterBL')
self.write_int16(0, 'hori padd')
# vert
self.write_int8(0, 'vert ascender')
self.write_int8(0, 'vert descender')
self.write_uint8(0, 'vert widthMax')
self.write_int8(0, 'vert caretSlopeNumerator')
self.write_int8(0, 'vert caretSlopeDenominator')
self.write_int8(0, 'vert caretOffset')
self.write_int8(0, 'vert minOriginSB')
self.write_int8(0, 'vert minAdvanceSB')
self.write_int8(0, 'vert maxBeforeBL')
self.write_int8(0, 'vert minAfterBL')
self.write_int16(0, 'vert padd')
# (bitmapSizeTable)
self.write_uint16(0, 'startGlyphIndex')
self.write_uint16(len(font.chars) - 1, 'endGlyphIndex')
self.write_uint8(font.bbx.height, 'ppemX')
self.write_uint8(font.bbx.height, 'ppemY')
self.write_uint8(1, 'bitDepth')
self.write_uint8(1, 'flags') # small metrics are horizontal
# indexSubTableArray
self.write_uint16(0, 'firstGlyphIndex')
self.write_uint16(len(font.chars) - 1, 'lastGlyphIndex')
self.write_uint32(BLOC_INDEX_ARRAY_SIZE, 'additionalOffsetToIndexSubtable')
# indexSubtableHeader
self.write_uint16(1 if font.proportional else 2, 'indexFormat')
self.write_uint16(1, 'imageFormat') # BDAT -> small metrics, byte-aligned
self.write_uint32(BDAT_HEADER_SIZE, 'imageDataOffset')
# indexSubtable data
if font.proportional:
offset = 0
for char in font.chars:
self.write_uint32(offset, 'offsetArray[]')
offset += BDAT.get_char_size(char)
self.write_uint32(offset, 'offsetArray[]')
else:
self.write_uint32(BDAT.get_char_size(font.chars[0]), 'imageSize')
self.write_uint8(font.bbx.height, 'height')
self.write_uint8(font.bbx.width, 'width')
self.write_int8(0, 'horiBearingX')
self.write_int8(font.px_ascender, 'horiBearingY')
self.write_uint8(font.bbx.width, 'horiAdvance')
self.write_int8(-(font.bbx.width >> 1), 'vertBearingX')
self.write_int8(0, 'vertBearingY')
self.write_uint8(font.bbx.height, 'vertAdvance')
# adjust
self.rewrite_uint32(self.size - BLOC_PREFIX_SIZE, BLOC_TABLE_SIZE_OFFSET)
# -- OS/2 --
OS_2_TABLE_SIZE = 96
class OS_2(Table): # pylint: disable=invalid-name
def __init__(self, font):
Table.__init__(self, 'OS/2')
# Version 4
x_avg_char_width = font.em_scale(font.avg_width) # otb1get.x_avg_char_width(font)
ul_char_ranges = otb1get.ul_char_ranges(font)
ul_code_pages = otb1get.ul_code_pages(font) if font.bmp_only else [0, 0]
# mostly from FontForge
script_xsize = font.em_scale(30, 100)
script_ysize = font.em_scale(40, 100)
subscript_yoff = script_ysize >> 1
xfactor = math.tan(font.italic_angle * math.pi / 180)
subscript_xoff = 0 # stub, no overlapping characters yet
superscript_yoff = font.em_ascender - script_ysize
superscript_xoff = -round(xfactor * superscript_yoff)
# write
self.write_uint16(4, 'version')
self.write_int16(x_avg_char_width, 'xAvgCharWidth')
self.write_uint16(700 if font.bold else 400, 'usWeightClass')
self.write_uint16(5, 'usWidthClass') # medium
self.write_int16(0, 'fsType')
self.write_int16(script_xsize, 'ySubscriptXSize')
self.write_int16(script_ysize, 'ySubscriptYSize')
self.write_int16(subscript_xoff, 'ySubscriptXOffset')
self.write_int16(subscript_yoff, 'ySubscriptYOffset')
self.write_int16(script_xsize, 'ySuperscriptXSize')
self.write_int16(script_ysize, 'ySuperscriptYSize')
self.write_int16(superscript_xoff, 'ySuperscriptXOffset')
self.write_int16(superscript_yoff, 'ySuperscriptYOffset')
self.write_int16(font.line_size, 'yStrikeoutSize')
self.write_int16(font.em_scale(25, 100), 'yStrikeoutPosition')
self.write_int16(0, 'sFamilyClass') # no classification
self.write_uint8(2, 'bFamilyType') # text and display
self.write_uint8(0, 'bSerifStyle') # any
self.write_uint8(8 if font.bold else 6, 'bWeight')
self.write_uint8(3 if font.proportional else 9, 'bProportion')
self.write_uint8(0, 'bContrast')
self.write_uint8(0, 'bStrokeVariation')
self.write_uint8(0, 'bArmStyle')
self.write_uint8(0, 'bLetterform')
self.write_uint8(0, 'bMidline')
self.write_uint8(0, 'bXHeight')
self.write_uint32(ul_char_ranges[0], 'ulCharRange1')
self.write_uint32(ul_char_ranges[1], 'ulCharRange2')
self.write_uint32(ul_char_ranges[2], 'ulCharRange3')
self.write_uint32(ul_char_ranges[3], 'ulCharRange4')
self.write_uint32(0x586F7334, 'achVendID') # 'Xos4'
self.write_uint16(OS_2.fs_selection(font), 'fsSelection')
self.write_uint16(min(font.min_code, fnutil.UNICODE_BMP_MAX), 'firstChar')
self.write_uint16(min(font.max_code, fnutil.UNICODE_BMP_MAX), 'lastChar')
self.write_int16(font.em_ascender, 'sTypoAscender')
self.write_int16(font.em_descender, 'sTypoDescender')
self.write_int16(font.params.line_gap, 'sTypoLineGap')
self.write_uint16(font.em_ascender, 'usWinAscent')
self.write_uint16(-font.em_descender, 'usWinDescent')
self.write_uint32(ul_code_pages[0], 'ulCodePageRange1')
self.write_uint32(ul_code_pages[1], 'ulCodePageRange2')
self.write_int16(font.em_scale(font.px_ascender * 0.6), 'sxHeight') # stub
self.write_int16(font.em_scale(font.px_ascender * 0.8), 'sCapHeight') # stub
self.write_uint16(OS_2.default_char(font), 'usDefaultChar')
self.write_uint16(OS_2.break_char(font), 'usBreakChar')
self.write_uint16(1, 'usMaxContext')
# check
self.check_size(OS_2_TABLE_SIZE)
@staticmethod
def break_char(font):
return 0x20 if next((char for char in font.chars if char.code == 0x20), None) else font.min_code
@staticmethod
def default_char(font):
if font.default_code != -1 and font.default_code <= fnutil.UNICODE_BMP_MAX:
return font.default_code
return 0 if font.min_code == 0 else font.max_code
@staticmethod
def fs_selection(font):
fs_selection = int(font.bold) * 5 + int(font.italic)
return fs_selection if fs_selection != 0 else 0x40 if font.xlfd[bdf.XLFD.SLANT] == 'R' else 0
# -- cmap --
CMAP_4_PREFIX_SIZE = 12
CMAP_4_FORMAT_SIZE = 16
CMAP_4_SEGMENT_SIZE = 8
CMAP_12_PREFIX_SIZE = 20
CMAP_12_FORMAT_SIZE = 16
CMAP_12_GROUP_SIZE = 12
class CMapRange:
def __init__(self, glyph_index=0, start_code=0, final_code=-2):
self.glyph_index = glyph_index
self.start_code = start_code
self.final_code = final_code
@property
def id_delta(self):
return (self.glyph_index - self.start_code) & 0xFFFF
class CMAP(Table):
def __init__(self, font):
Table.__init__(self, 'cmap')
# make ranges
ranges = []
range = CMapRange()
index = -1
for char in font.chars:
index += 1
code = char.code
if code == range.final_code + 1:
range.final_code += 1
else:
range = CMapRange(index, code, code)
ranges.append(range)
# write
if font.bmp_only:
if font.max_code < 0xFFFF:
ranges.append(CMapRange(0, 0xFFFF, 0xFFFF))
self.write_format_4(ranges)
else:
self.write_format_12(ranges)
def write_format_4(self, ranges):
# index
self.write_uint16(0, 'version')
self.write_uint16(1, 'numberSubtables')
# encoding subtables index
self.write_uint16(3, 'platformID') # Microsoft
self.write_uint16(1, 'platformSpecificID') # Unicode BMP (UCS-2)
self.write_uint32(CMAP_4_PREFIX_SIZE, 'offset') # for Unicode BMP (UCS-2)
# cmap format 4
seg_count = len(ranges)
subtable_size = CMAP_4_FORMAT_SIZE + seg_count * CMAP_4_SEGMENT_SIZE
search_range = 2 << math.floor(math.log2(seg_count))
self.write_uint16(4, 'format')
self.write_uint16(subtable_size, 'length')
self.write_uint16(0, 'language') # none/independent
self.write_uint16(seg_count * 2, 'segCountX2')
self.write_uint16(search_range, 'searchRange')
self.write_uint16(int(math.log2(search_range / 2)), 'entrySelector')
self.write_uint16((seg_count * 2) - search_range, 'rangeShift')
for range in ranges:
self.write_uint16(range.final_code, 'endCode')
self.write_uint16(0, 'reservedPad')
for range in ranges:
self.write_uint16(range.start_code, 'startCode')
for range in ranges:
self.write_uint16(range.id_delta, 'idDelta')
for _ in ranges:
self.write_uint16(0, 'idRangeOffset')
# check
self.check_size(CMAP_4_PREFIX_SIZE + subtable_size)
def write_format_12(self, ranges):
# index
self.write_uint16(0, 'version')
self.write_uint16(2, 'numberSubtables')
# encoding subtables
self.write_uint16(0, 'platformID') # Unicode
self.write_uint16(4, 'platformSpecificID') # Unicode 2.0+ full range
self.write_uint32(CMAP_12_PREFIX_SIZE, 'offset') # for Unicode 2.0+ full range
self.write_uint16(3, 'platformID') # Microsoft
self.write_uint16(10, 'platformSpecificID') # Unicode UCS-4
self.write_uint32(CMAP_12_PREFIX_SIZE, 'offset') # for Unicode UCS-4
# cmap format 12
subtable_size = CMAP_12_FORMAT_SIZE + len(ranges) * CMAP_12_GROUP_SIZE
self.write_fixed(12, 'format')
self.write_uint32(subtable_size, 'length')
self.write_uint32(0, 'language') # none/independent
self.write_uint32(len(ranges), 'nGroups')
for range in ranges:
self.write_uint32(range.start_code, 'startCharCode')
self.write_uint32(range.final_code, 'endCharCode')
self.write_uint32(range.glyph_index, 'startGlyphID')
# check
self.check_size(CMAP_12_PREFIX_SIZE + subtable_size)
# -- glyf --
class GLYF(Table):
def __init__(self, _font):
Table.__init__(self, 'glyf')
# -- head --
HEAD_TABLE_SIZE = 54
HEAD_CHECKSUM_OFFSET = 8
class HEAD(Table):
def __init__(self, font):
Table.__init__(self, 'head')
self.write_fixed(1, 'version')
self.write_fixed(1, 'fontRevision')
self.write_uint32(0, 'checksumAdjustment') # adjusted later
self.write_uint32(0x5F0F3CF5, 'magicNumber')
self.write_uint16(HEAD.flags(font), 'flags')
self.write_uint16(font.params.em_size, 'unitsPerEm')
self.write_uint64(font.created, 'created')
self.write_uint64(font.modified, 'modified')
self.write_int16(0, 'xMin')
self.write_int16(font.em_descender, 'yMin')
self.write_int16(font.em_max_width, 'xMax')
self.write_int16(font.em_ascender, 'yMax')
self.write_uint16(font.mac_style, 'macStyle')
self.write_uint16(font.params.low_ppem or font.bbx.height, 'lowestRecPPEM')
self.write_int16(font.params.dir_hint, 'fontDirectionHint')
self.write_int16(0, 'indexToLocFormat') # short
self.write_int16(0, 'glyphDataFormat') # current
# check
self.check_size(HEAD_TABLE_SIZE)
@staticmethod
def flags(font):
return 0x20B if otb1get.contains_rtl(font) else 0x0B # y0 base, x0 lsb, scale int
# -- hhea --
HHEA_TABLE_SIZE = 36
class HHEA(Table):
def __init__(self, font):
Table.__init__(self, 'hhea')
self.write_fixed(1, 'version')
self.write_int16(font.em_ascender, 'ascender')
self.write_int16(font.em_descender, 'descender')
self.write_int16(font.params.line_gap, 'lineGap')
self.write_uint16(font.em_max_width, 'advanceWidthMax')
self.write_int16(0, 'minLeftSideBearing')
self.write_int16(0, 'minRightSideBearing')
self.write_int16(font.x_max_extent, 'xMaxExtent')
self.write_int16(100 if font.italic else 1, 'caretSlopeRise')
self.write_int16(20 if font.italic else 0, 'caretSlopeRun')
self.write_int16(0, 'caretOffset')
self.write_int16(0, 'reserved')
self.write_int16(0, 'reserved')
self.write_int16(0, 'reserved')
self.write_int16(0, 'reserved')
self.write_int16(0, 'metricDataFormat') # current
self.write_uint16(len(font.chars), 'numOfLongHorMetrics')
# check
self.check_size(HHEA_TABLE_SIZE)
# -- hmtx --
class HMTX(Table):
def __init__(self, font):
Table.__init__(self, 'hmtx')
for char in font.chars:
self.write_uint16(font.em_scale_width(char), 'advanceWidth')
self.write_int16(0, 'leftSideBearing')
# -- loca --
class LOCA(Table):
def __init__(self, font):
Table.__init__(self, 'loca')
if not font.params.single_loca:
for _ in font.chars:
self.write_uint16(0, 'offset')
self.write_uint16(0, 'offset')
# -- maxp --
MAXP_TABLE_SIZE = 32
class MAXP(Table):
def __init__(self, font):
Table.__init__(self, 'maxp')
self.write_fixed(1, 'version')
self.write_uint16(len(font.chars), 'numGlyphs')
self.write_uint16(0, 'maxPoints')
self.write_uint16(0, 'maxContours')
self.write_uint16(0, 'maxComponentPoints')
self.write_uint16(0, 'maxComponentContours')
self.write_uint16(2, 'maxZones')
self.write_uint16(0, 'maxTwilightPoints')
self.write_uint16(1, 'maxStorage')
self.write_uint16(1, 'maxFunctionDefs')
self.write_uint16(0, 'maxInstructionDefs')
self.write_uint16(64, 'maxStackElements')
self.write_uint16(0, 'maxSizeOfInstructions')
self.write_uint16(0, 'maxComponentElements')
self.write_uint16(0, 'maxComponentDepth')
# check
self.check_size(MAXP_TABLE_SIZE)
# -- name --
@unique # pylint: disable=invalid-name
class NAME_ID(IntEnum): # pylint: disable=invalid-name
COPYRIGHT = 0
FONT_FAMILY = 1
FONT_SUBFAMILY = 2
UNIQUE_SUBFAMILY = 3
FULL_FONT_NAME = 4
LICENSE = 14
NAME_HEADER_SIZE = 6
NAME_RECORD_SIZE = 12
class NAME(Table):
def __init__(self, font):
Table.__init__(self, 'name')
# compute names
names = OrderedDict()
copyright = font.props.get('COPYRIGHT')
if copyright is not None:
names[NAME_ID.COPYRIGHT] = fnutil.unquote(copyright)
family = font.xlfd[bdf.XLFD.FAMILY_NAME]
style = [b'Regular', b'Bold', b'Italic', b'Bold Italic'][font.mac_style]
names[NAME_ID.FONT_FAMILY] = family
names[NAME_ID.FONT_SUBFAMILY] = style
names[NAME_ID.UNIQUE_SUBFAMILY] = b'%s %s bitmap height %d' % (family, style, font.bbx.height)
names[NAME_ID.FULL_FONT_NAME] = b'%s %s' % (family, style)
license = font.props.get('LICENSE')
notice = font.props.get('NOTICE')
if license is None and notice is not None and b'license' in notice.lower():
license = notice
if license is not None:
names[NAME_ID.LICENSE] = fnutil.unquote(license)
# header
count = len(names) * (1 + 1) # Unicode + Microsoft
string_offset = NAME_HEADER_SIZE + NAME_RECORD_SIZE * count
self.write_uint16(0, 'format')
self.write_uint16(count, 'count')
self.write_uint16(string_offset, 'stringOffset')
# name records / create values
values = Table('name')
for [name_id, bstr] in names.items():
s = font.decode(bstr)
value = codecs.encode(s, 'utf_16_be')
bmp = font.bmp_only and len(value) == len(s) * 2
# Unicode
self.write_uint16(0, 'platformID') # Unicode
self.write_uint16(3 if bmp else 4, 'platformSpecificID')
self.write_uint16(0, 'languageID') # none
self.write_uint16(name_id, 'nameID')
self.write_uint16(len(value), 'length') # in bytes
self.write_uint16(values.size, 'offset')
# Microsoft
self.write_uint16(3, 'platformID') # Microsoft
self.write_uint16(1 if bmp else 10, 'platformSpecificID')
self.write_uint16(font.params.w_lang_id, 'languageID')
self.write_uint16(name_id, 'nameID')
self.write_uint16(len(value), 'length') # in bytes
self.write_uint16(values.size, 'offset')
# value
values.write(value)
# write values
self.write_table(values)
# check
self.check_size(string_offset + values.size)
# -- post --
POST_TABLE_SIZE = 32
class POST(Table):
def __init__(self, font):
Table.__init__(self, 'post')
self.write_fixed(2 if font.params.post_names else 3, 'format')
self.write_fixed(font.italic_angle, 'italicAngle')
self.write_int16(font.underline_position, 'underlinePosition')
self.write_int16(font.line_size, 'underlineThickness')
self.write_uint32(0 if font.proportional else 1, 'isFixedPitch')
self.write_uint32(0, 'minMemType42')
self.write_uint32(0, 'maxMemType42')
self.write_uint32(0, 'minMemType1')
self.write_uint32(0, 'maxMemType1')
# names
if font.params.post_names:
self.write_uint16(len(font.chars), 'numberOfGlyphs')
post_names = otb1get.post_mac_names()
post_mac_count = len(post_names)
for name in [char.props['STARTCHAR'] for char in font.chars]:
if name in post_names:
self.write_uint16(post_names.index(name), 'glyphNameIndex')
else:
self.write_uint16(len(post_names), 'glyphNameIndex')
post_names.append(name)
for name in post_names[post_mac_count:]:
self.write_uint8(len(name), 'glyphNameLength')
self.write(name)
# check
else:
self.check_size(POST_TABLE_SIZE)
# -- SFNT --
SFNT_HEADER_SIZE = 12
SFNT_RECORD_SIZE = 16
SFNT_SUBTABLES = (BDAT, BLOC, OS_2, CMAP, GLYF, HEAD, HHEA, HMTX, LOCA, MAXP, NAME, POST)
class SFNT(Table):
def __init__(self, font):
Table.__init__(self, 'SFNT')
# create tables
tables = []
for ctor in SFNT_SUBTABLES:
tables.append(ctor(font))
# header
num_tables = len(tables)
entry_selector = math.floor(math.log2(num_tables))
search_range = 16 << entry_selector
content_offset = SFNT_HEADER_SIZE + num_tables * SFNT_RECORD_SIZE
offset = content_offset
content = Table('SFNT')
head_checksum_offset = -1
self.write_fixed(1, 'sfntVersion')
self.write_uint16(num_tables, 'numTables')
self.write_uint16(search_range, 'searchRange')
self.write_uint16(entry_selector, 'entrySelector')
self.write_uint16(num_tables * 16 - search_range, 'rangeShift')
# table records / create content
for table in tables:
self.write(bytes(table.table_name, 'ascii'))
self.write_uint32(table.checksum(), 'checkSum')
self.write_uint32(offset, 'offset')
self.write_uint32(len(table.data), 'length')
# create content
if table.table_name == 'head':
head_checksum_offset = offset + HEAD_CHECKSUM_OFFSET
padded_data = table.data + table.padding
content.write(padded_data)
offset += len(padded_data)
# write content
self.write_table(content)
# check
self.check_size(content_offset + len(content.data))
# adjust
if head_checksum_offset != -1:
self.rewrite_uint32((0xB1B0AFBA - self.checksum()) & 0xFFFFFFFF, head_checksum_offset)
+706
View File
@@ -0,0 +1,706 @@
/*
Copyright (C) 2018-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
// -- xAvgCharWidth --
const WEIGHT_FACTORS = [
[ 'a', 64 ],
[ 'b', 14 ],
[ 'c', 27 ],
[ 'd', 35 ],
[ 'e', 100 ],
[ 'f', 20 ],
[ 'g', 14 ],
[ 'h', 42 ],
[ 'i', 63 ],
[ 'j', 3 ],
[ 'k', 6 ],
[ 'l', 35 ],
[ 'm', 20 ],
[ 'n', 56 ],
[ 'o', 56 ],
[ 'p', 17 ],
[ 'q', 4 ],
[ 'r', 49 ],
[ 's', 56 ],
[ 't', 71 ],
[ 'u', 31 ],
[ 'v', 10 ],
[ 'w', 18 ],
[ 'x', 3 ],
[ 'y', 18 ],
[ 'z', 2 ],
[ ' ', 166 ]
];
function xAvgCharWidth(font) {
let xAvgTotalWidth = 0;
for (let factor of WEIGHT_FACTORS) {
const char = font.chars.find(_char => _char.code === factor[0].charCodeAt(0));
if (char == null) {
return 0;
}
xAvgTotalWidth += font.scaleWidth(char) * factor[1];
}
return fnutil.round(xAvgTotalWidth / 1000);
}
// -- ulCharRanges --
const CHAR_RANGES = [
[ 0, 0x0000, 0x007F ],
[ 1, 0x0080, 0x00FF ],
[ 2, 0x0100, 0x017F ],
[ 3, 0x0180, 0x024F ],
[ 4, 0x0250, 0x02AF ],
[ 4, 0x1D00, 0x1D7F ],
[ 4, 0x1D80, 0x1DBF ],
[ 5, 0x02B0, 0x02FF ],
[ 5, 0xA700, 0xA71F ],
[ 6, 0x0300, 0x036F ],
[ 6, 0x1DC0, 0x1DFF ],
[ 7, 0x0370, 0x03FF ],
[ 8, 0x2C80, 0x2CFF ],
[ 9, 0x0400, 0x04FF ],
[ 9, 0x0500, 0x052F ],
[ 9, 0x2DE0, 0x2DFF ],
[ 9, 0xA640, 0xA69F ],
[ 10, 0x0530, 0x058F ],
[ 11, 0x0590, 0x05FF ],
[ 12, 0xA500, 0xA63F ],
[ 13, 0x0600, 0x06FF ],
[ 13, 0x0750, 0x077F ],
[ 14, 0x07C0, 0x07FF ],
[ 15, 0x0900, 0x097F ],
[ 16, 0x0980, 0x09FF ],
[ 17, 0x0A00, 0x0A7F ],
[ 18, 0x0A80, 0x0AFF ],
[ 19, 0x0B00, 0x0B7F ],
[ 20, 0x0B80, 0x0BFF ],
[ 21, 0x0C00, 0x0C7F ],
[ 22, 0x0C80, 0x0CFF ],
[ 23, 0x0D00, 0x0D7F ],
[ 24, 0x0E00, 0x0E7F ],
[ 25, 0x0E80, 0x0EFF ],
[ 26, 0x10A0, 0x10FF ],
[ 26, 0x2D00, 0x2D2F ],
[ 27, 0x1B00, 0x1B7F ],
[ 28, 0x1100, 0x11FF ],
[ 29, 0x1E00, 0x1EFF ],
[ 29, 0x2C60, 0x2C7F ],
[ 29, 0xA720, 0xA7FF ],
[ 30, 0x1F00, 0x1FFF ],
[ 31, 0x2000, 0x206F ],
[ 31, 0x2E00, 0x2E7F ],
[ 32, 0x2070, 0x209F ],
[ 33, 0x20A0, 0x20CF ],
[ 34, 0x20D0, 0x20FF ],
[ 35, 0x2100, 0x214F ],
[ 36, 0x2150, 0x218F ],
[ 37, 0x2190, 0x21FF ],
[ 37, 0x27F0, 0x27FF ],
[ 37, 0x2900, 0x297F ],
[ 37, 0x2B00, 0x2BFF ],
[ 38, 0x2200, 0x22FF ],
[ 38, 0x2A00, 0x2AFF ],
[ 38, 0x27C0, 0x27EF ],
[ 38, 0x2980, 0x29FF ],
[ 39, 0x2300, 0x23FF ],
[ 40, 0x2400, 0x243F ],
[ 41, 0x2440, 0x245F ],
[ 42, 0x2460, 0x24FF ],
[ 43, 0x2500, 0x257F ],
[ 44, 0x2580, 0x259F ],
[ 45, 0x25A0, 0x25FF ],
[ 46, 0x2600, 0x26FF ],
[ 47, 0x2700, 0x27BF ],
[ 48, 0x3000, 0x303F ],
[ 49, 0x3040, 0x309F ],
[ 50, 0x30A0, 0x30FF ],
[ 50, 0x31F0, 0x31FF ],
[ 51, 0x3100, 0x312F ],
[ 51, 0x31A0, 0x31BF ],
[ 52, 0x3130, 0x318F ],
[ 53, 0xA840, 0xA87F ],
[ 54, 0x3200, 0x32FF ],
[ 55, 0x3300, 0x33FF ],
[ 56, 0xAC00, 0xD7AF ],
[ 57, 0xD800, 0xDFFF ],
[ 58, 0x10900, 0x1091F ],
[ 59, 0x4E00, 0x9FFF ],
[ 59, 0x2E80, 0x2EFF ],
[ 59, 0x2F00, 0x2FDF ],
[ 59, 0x2FF0, 0x2FFF ],
[ 59, 0x3400, 0x4DBF ],
[ 59, 0x20000, 0x2A6DF ],
[ 59, 0x3190, 0x319F ],
[ 60, 0xE000, 0xF8FF ],
[ 61, 0x31C0, 0x31EF ],
[ 61, 0xF900, 0xFAFF ],
[ 61, 0x2F800, 0x2FA1F ],
[ 62, 0xFB00, 0xFB4F ],
[ 63, 0xFB50, 0xFDFF ],
[ 64, 0xFE20, 0xFE2F ],
[ 65, 0xFE10, 0xFE1F ],
[ 65, 0xFE30, 0xFE4F ],
[ 66, 0xFE50, 0xFE6F ],
[ 67, 0xFE70, 0xFEFF ],
[ 68, 0xFF00, 0xFFEF ],
[ 69, 0xFFF0, 0xFFFF ],
[ 70, 0x0F00, 0x0FFF ],
[ 71, 0x0700, 0x074F ],
[ 72, 0x0780, 0x07BF ],
[ 73, 0x0D80, 0x0DFF ],
[ 74, 0x1000, 0x109F ],
[ 75, 0x1200, 0x137F ],
[ 75, 0x1380, 0x139F ],
[ 75, 0x2D80, 0x2DDF ],
[ 76, 0x13A0, 0x13FF ],
[ 77, 0x1400, 0x167F ],
[ 78, 0x1680, 0x169F ],
[ 79, 0x16A0, 0x16FF ],
[ 80, 0x1780, 0x17FF ],
[ 80, 0x19E0, 0x19FF ],
[ 81, 0x1800, 0x18AF ],
[ 82, 0x2800, 0x28FF ],
[ 83, 0xA000, 0xA48F ],
[ 83, 0xA490, 0xA4CF ],
[ 84, 0x1700, 0x171F ],
[ 84, 0x1720, 0x173F ],
[ 84, 0x1740, 0x175F ],
[ 84, 0x1760, 0x177F ],
[ 85, 0x10300, 0x1032F ],
[ 86, 0x10330, 0x1034F ],
[ 87, 0x10400, 0x1044F ],
[ 88, 0x1D000, 0x1D0FF ],
[ 88, 0x1D100, 0x1D1FF ],
[ 88, 0x1D200, 0x1D24F ],
[ 89, 0x1D400, 0x1D7FF ],
[ 90, 0xF0000, 0xFFFFD ],
[ 90, 0x100000, 0x10FFFD ],
[ 91, 0xFE00, 0xFE0F ],
[ 91, 0xE0100, 0xE01EF ],
[ 92, 0xE0000, 0xE007F ],
[ 93, 0x1900, 0x194F ],
[ 94, 0x1950, 0x197F ],
[ 95, 0x1980, 0x19DF ],
[ 96, 0x1A00, 0x1A1F ],
[ 97, 0x2C00, 0x2C5F ],
[ 98, 0x2D30, 0x2D7F ],
[ 99, 0x4DC0, 0x4DFF ],
[ 100, 0xA800, 0xA82F ],
[ 101, 0x10000, 0x1007F ],
[ 101, 0x10080, 0x100FF ],
[ 101, 0x10100, 0x1013F ],
[ 102, 0x10140, 0x1018F ],
[ 103, 0x10380, 0x1039F ],
[ 104, 0x103A0, 0x103DF ],
[ 105, 0x10450, 0x1047F ],
[ 106, 0x10480, 0x104AF ],
[ 107, 0x10800, 0x1083F ],
[ 108, 0x10A00, 0x10A5F ],
[ 109, 0x1D300, 0x1D35F ],
[ 110, 0x12000, 0x123FF ],
[ 110, 0x12400, 0x1247F ],
[ 111, 0x1D360, 0x1D37F ],
[ 112, 0x1B80, 0x1BBF ],
[ 113, 0x1C00, 0x1C4F ],
[ 114, 0x1C50, 0x1C7F ],
[ 115, 0xA880, 0xA8DF ],
[ 116, 0xA900, 0xA92F ],
[ 117, 0xA930, 0xA95F ],
[ 118, 0xAA00, 0xAA5F ],
[ 119, 0x10190, 0x101CF ],
[ 120, 0x101D0, 0x101FF ],
[ 121, 0x102A0, 0x102DF ],
[ 121, 0x10280, 0x1029F ],
[ 121, 0x10920, 0x1093F ],
[ 122, 0x1F030, 0x1F09F ],
[ 122, 0x1F000, 0x1F02F ]
];
function ulCharRanges(font) {
let charRanges = [0, 0, 0, 0];
font.chars.forEach(char => {
const unicode = char.code;
const range = CHAR_RANGES.find(_range => unicode >= _range[1] && unicode <= _range[2]);
if (range != null) {
charRanges[range[0] >> 5] |= 1 << (range[0] & 0x1F);
}
});
if (font.maxCode >= 0x10000) {
charRanges[57 >> 5] |= 1 << (57 & 0x1F);
}
return [ charRanges[0] >>> 0, charRanges[1] >>> 0, charRanges[2] >>> 0, charRanges[3] >>> 0 ];
}
// -- ulCodePages --
function ulCodePages(font) {
const spaceIndex = font.chars.findIndex(char => char.code === 0x20);
const ascii = Number(spaceIndex !== -1 && font.chars[spaceIndex + 0x5E].code === 0x7E);
const findf = (unicode) => Number(font.chars.findIndex(char => char.code === unicode) !== -1);
const graph = findf(0x2524);
const radic = findf(0x221A);
let codePages = [0, 0];
// conditions from FontForge
font.chars.forEach(char => {
switch (char.code) {
case 0x00DE:
codePages[0] |= (ascii) << 0; // 1252 Latin1
break;
case 0x255A:
codePages[1] |= (ascii) << 30; // 850 WE/Latin1
codePages[1] |= (ascii) << 31; // 437 US
break;
case 0x013D:
codePages[0] |= (ascii) << 1; // 1250 Latin 2: Eastern Europe
codePages[1] |= (ascii & graph) << 26; // 852 Latin 2
break;
case 0x0411:
codePages[0] |= 1 << 2; // 1251 Cyrillic
codePages[1] |= (findf(0x255C) & graph) << 17; // 866 MS-DOS Russian
codePages[1] |= (findf(0x0405) & graph) << 25; // 855 IBM Cyrillic
break;
case 0x0386:
codePages[0] |= 1 << 3; // 1253 Greek
codePages[1] |= (findf(0x00BD) & graph) << 16; // 869 IBM Greek
codePages[1] |= (graph & radic) << 28; // 737 Greek; former 437 G
break;
case 0x0130:
codePages[0] |= (ascii) << 4; // 1254 Turkish
codePages[1] |= (ascii & graph) << 24; // 857 IBM Turkish
break;
case 0x05D0:
codePages[0] |= 1 << 5; // 1255 Hebrew
codePages[1] |= (graph & radic) << 21; // 862 Hebrew
break;
case 0x0631:
codePages[0] |= 1 << 6; // 1256 Arabic
codePages[1] |= (radic) << 19; // 864 Arabic
codePages[1] |= (graph) << 29; // 708 Arabic; ASMO 708
break;
case 0x0157:
codePages[0] |= (ascii) << 7; // 1257 Windows Baltic
codePages[1] |= (ascii & graph) << 27; // 775 MS-DOS Baltic
break;
case 0x20AB:
codePages[0] |= 1 << 8; // 1258 Vietnamese
break;
case 0x0E45:
codePages[0] |= 1 << 16; // 874 Thai
break;
case 0x30A8:
codePages[0] |= 1 << 17; // 932 JIS/Japan
break;
case 0x3105:
codePages[0] |= 1 << 18; // 936 Chinese: Simplified chars
break;
case 0x3131:
codePages[0] |= 1 << 19; // 949 Korean Wansung
break;
case 0x592E:
codePages[0] |= 1 << 20; // 950 Chinese: Traditional chars
break;
case 0xACF4:
codePages[0] |= 1 << 21; // 1361 Korean Johab
break;
case 0x2030:
codePages[0] |= (findf(0x2211) & ascii) << 29; // Macintosh Character Set (Roman)
break;
case 0x2665:
codePages[0] |= (ascii) << 30; // OEM Character Set
break;
case 0x00C5:
codePages[1] |= (ascii & graph & radic) << 18; // 865 MS-DOS Nordic
break;
case 0x00E9:
codePages[1] |= (ascii & graph & radic) << 20; // 863 MS-DOS Canadian French
break;
case 0x00F5:
codePages[1] |= (ascii & graph & radic) << 23; // 860 MS-DOS Portuguese
break;
case 0x00FE:
codePages[1] |= (ascii & graph) << 22; // 861 MS-DOS Icelandic
break;
default :
if (char.code >= 0xF000 && char.code <= 0xF0FF) {
codePages[0] |= 1 << 31; // Symbol Character Set
}
break;
}
});
return [ codePages[0] >>> 0, codePages[1] >>> 0 ];
}
// -- containsRTL --
const RTL_RANGES = [
[ 0x05BE, 0x05BE ],
[ 0x05C0, 0x05C0 ],
[ 0x05C3, 0x05C3 ],
[ 0x05C6, 0x05C6 ],
[ 0x05D0, 0x05EA ],
[ 0x05EF, 0x05F4 ],
[ 0x0608, 0x0608 ],
[ 0x060B, 0x060B ],
[ 0x060D, 0x060D ],
[ 0x061B, 0x061C ],
[ 0x061E, 0x064A ],
[ 0x066D, 0x066F ],
[ 0x0671, 0x06D5 ],
[ 0x06E5, 0x06E6 ],
[ 0x06EE, 0x06EF ],
[ 0x06FA, 0x070D ],
[ 0x070F, 0x0710 ],
[ 0x0712, 0x072F ],
[ 0x074D, 0x07A5 ],
[ 0x07B1, 0x07B1 ],
[ 0x07C0, 0x07EA ],
[ 0x07F4, 0x07F5 ],
[ 0x07FA, 0x07FA ],
[ 0x07FE, 0x0815 ],
[ 0x081A, 0x081A ],
[ 0x0824, 0x0824 ],
[ 0x0828, 0x0828 ],
[ 0x0830, 0x083E ],
[ 0x0840, 0x0858 ],
[ 0x085E, 0x085E ],
[ 0x0860, 0x086A ],
[ 0x08A0, 0x08B4 ],
[ 0x08B6, 0x08BD ],
[ 0x200F, 0x200F ],
[ 0x202B, 0x202B ],
[ 0x202E, 0x202E ],
[ 0xFB1D, 0xFB1D ],
[ 0xFB1F, 0xFB28 ],
[ 0xFB2A, 0xFB36 ],
[ 0xFB38, 0xFB3C ],
[ 0xFB3E, 0xFB3E ],
[ 0xFB40, 0xFB41 ],
[ 0xFB43, 0xFB44 ],
[ 0xFB46, 0xFBC1 ],
[ 0xFBD3, 0xFD3D ],
[ 0xFD50, 0xFD8F ],
[ 0xFD92, 0xFDC7 ],
[ 0xFDF0, 0xFDFC ],
[ 0xFE70, 0xFE74 ],
[ 0xFE76, 0xFEFC ],
[ 0x10800, 0x10FFF ],
[ 0x1E800, 0x1EFFF ],
[ -1, 0 ]
];
function containsRTL(font) {
let index = 0;
for (let char of font.chars) {
while (char.code > RTL_RANGES[index][1]) {
if (RTL_RANGES[++index][0] === -1) {
break;
}
}
if (char.code >= RTL_RANGES[index][0]) {
return 0x200;
}
}
return 0x000;
}
// -- postMacIndex --
const POST_MAC_NAMES = [
'.notdef',
'.null',
'nonmarkingreturn',
'space',
'exclam',
'quotedbl',
'numbersign',
'dollar',
'percent',
'ampersand',
'quotesingle',
'parenleft',
'parenright',
'asterisk',
'plus',
'comma',
'hyphen',
'period',
'slash',
'zero',
'one',
'two',
'three',
'four',
'five',
'six',
'seven',
'eight',
'nine',
'colon',
'semicolon',
'less',
'equal',
'greater',
'question',
'at',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
'bracketleft',
'backslash',
'bracketright',
'asciicircum',
'underscore',
'grave',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'braceleft',
'bar',
'braceright',
'asciitilde',
'Adieresis',
'Aring',
'Ccedilla',
'Eacute',
'Ntilde',
'Odieresis',
'Udieresis',
'aacute',
'agrave',
'acircumflex',
'adieresis',
'atilde',
'aring',
'ccedilla',
'eacute',
'egrave',
'ecircumflex',
'edieresis',
'iacute',
'igrave',
'icircumflex',
'idieresis',
'ntilde',
'oacute',
'ograve',
'ocircumflex',
'odieresis',
'otilde',
'uacute',
'ugrave',
'ucircumflex',
'udieresis',
'dagger',
'degree',
'cent',
'sterling',
'section',
'bullet',
'paragraph',
'germandbls',
'registered',
'copyright',
'trademark',
'acute',
'dieresis',
'notequal',
'AE',
'Oslash',
'infinity',
'plusminus',
'lessequal',
'greaterequal',
'yen',
'mu',
'partialdiff',
'summation',
'product',
'pi',
'integral',
'ordfeminine',
'ordmasculine',
'Omega',
'ae',
'oslash',
'questiondown',
'exclamdown',
'logicalnot',
'radical',
'florin',
'approxequal',
'Delta',
'guillemotleft',
'guillemotright',
'ellipsis',
'nonbreakingspace',
'Agrave',
'Atilde',
'Otilde',
'OE',
'oe',
'endash',
'emdash',
'quotedblleft',
'quotedblright',
'quoteleft',
'quoteright',
'divide',
'lozenge',
'ydieresis',
'Ydieresis',
'fraction',
'currency',
'guilsinglleft',
'guilsinglright',
'fi',
'fl',
'daggerdbl',
'periodcentered',
'quotesinglbase',
'quotedblbase',
'perthousand',
'Acircumflex',
'Ecircumflex',
'Aacute',
'Edieresis',
'Egrave',
'Iacute',
'Icircumflex',
'Idieresis',
'Igrave',
'Oacute',
'Ocircumflex',
'apple',
'Ograve',
'Uacute',
'Ucircumflex',
'Ugrave',
'dotlessi',
'circumflex',
'tilde',
'macron',
'breve',
'dotaccent',
'ring',
'cedilla',
'hungarumlaut',
'ogonek',
'caron',
'Lslash',
'lslash',
'Scaron',
'scaron',
'Zcaron',
'zcaron',
'brokenbar',
'Eth',
'eth',
'Yacute',
'yacute',
'Thorn',
'thorn',
'minus',
'multiply',
'onesuperior',
'twosuperior',
'threesuperior',
'onehalf',
'onequarter',
'threequarters',
'franc',
'Gbreve',
'gbreve',
'Idotaccent',
'Scedilla',
'scedilla',
'Cacute',
'cacute',
'Ccaron',
'ccaron',
'dcroat'
];
function postMacNames() {
return POST_MAC_NAMES.slice();
}
// -- Export --
module.exports = Object.freeze({
xAvgCharWidth,
ulCharRanges,
ulCodePages,
containsRTL,
postMacNames
});
+663
View File
@@ -0,0 +1,663 @@
#
# Copyright (C) 2018-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# pylint: disable=bad-whitespace
# -- x_avg_char_width --
WEIGHT_FACTORS = (
( 'a', 64 ),
( 'b', 14 ),
( 'c', 27 ),
( 'd', 35 ),
( 'e', 100 ),
( 'f', 20 ),
( 'g', 14 ),
( 'h', 42 ),
( 'i', 63 ),
( 'j', 3 ),
( 'k', 6 ),
( 'l', 35 ),
( 'm', 20 ),
( 'n', 56 ),
( 'o', 56 ),
( 'p', 17 ),
( 'q', 4 ),
( 'r', 49 ),
( 's', 56 ),
( 't', 71 ),
( 'u', 31 ),
( 'v', 10 ),
( 'w', 18 ),
( 'x', 3 ),
( 'y', 18 ),
( 'z', 2 ),
( ' ', 166 )
)
def x_avg_char_width(font):
x_avg_total_width = 0
for factor in WEIGHT_FACTORS:
char = next((char for char in font.chars if char.code == ord(factor[0])), None)
if char is None:
return 0
x_avg_total_width += font.scaleWidth(char) * factor[1]
return round(x_avg_total_width / 1000)
# -- ul_char_ranges --
CHAR_RANGES = (
( 0, 0x0000, 0x007F ),
( 1, 0x0080, 0x00FF ),
( 2, 0x0100, 0x017F ),
( 3, 0x0180, 0x024F ),
( 4, 0x0250, 0x02AF ),
( 4, 0x1D00, 0x1D7F ),
( 4, 0x1D80, 0x1DBF ),
( 5, 0x02B0, 0x02FF ),
( 5, 0xA700, 0xA71F ),
( 6, 0x0300, 0x036F ),
( 6, 0x1DC0, 0x1DFF ),
( 7, 0x0370, 0x03FF ),
( 8, 0x2C80, 0x2CFF ),
( 9, 0x0400, 0x04FF ),
( 9, 0x0500, 0x052F ),
( 9, 0x2DE0, 0x2DFF ),
( 9, 0xA640, 0xA69F ),
( 10, 0x0530, 0x058F ),
( 11, 0x0590, 0x05FF ),
( 12, 0xA500, 0xA63F ),
( 13, 0x0600, 0x06FF ),
( 13, 0x0750, 0x077F ),
( 14, 0x07C0, 0x07FF ),
( 15, 0x0900, 0x097F ),
( 16, 0x0980, 0x09FF ),
( 17, 0x0A00, 0x0A7F ),
( 18, 0x0A80, 0x0AFF ),
( 19, 0x0B00, 0x0B7F ),
( 20, 0x0B80, 0x0BFF ),
( 21, 0x0C00, 0x0C7F ),
( 22, 0x0C80, 0x0CFF ),
( 23, 0x0D00, 0x0D7F ),
( 24, 0x0E00, 0x0E7F ),
( 25, 0x0E80, 0x0EFF ),
( 26, 0x10A0, 0x10FF ),
( 26, 0x2D00, 0x2D2F ),
( 27, 0x1B00, 0x1B7F ),
( 28, 0x1100, 0x11FF ),
( 29, 0x1E00, 0x1EFF ),
( 29, 0x2C60, 0x2C7F ),
( 29, 0xA720, 0xA7FF ),
( 30, 0x1F00, 0x1FFF ),
( 31, 0x2000, 0x206F ),
( 31, 0x2E00, 0x2E7F ),
( 32, 0x2070, 0x209F ),
( 33, 0x20A0, 0x20CF ),
( 34, 0x20D0, 0x20FF ),
( 35, 0x2100, 0x214F ),
( 36, 0x2150, 0x218F ),
( 37, 0x2190, 0x21FF ),
( 37, 0x27F0, 0x27FF ),
( 37, 0x2900, 0x297F ),
( 37, 0x2B00, 0x2BFF ),
( 38, 0x2200, 0x22FF ),
( 38, 0x2A00, 0x2AFF ),
( 38, 0x27C0, 0x27EF ),
( 38, 0x2980, 0x29FF ),
( 39, 0x2300, 0x23FF ),
( 40, 0x2400, 0x243F ),
( 41, 0x2440, 0x245F ),
( 42, 0x2460, 0x24FF ),
( 43, 0x2500, 0x257F ),
( 44, 0x2580, 0x259F ),
( 45, 0x25A0, 0x25FF ),
( 46, 0x2600, 0x26FF ),
( 47, 0x2700, 0x27BF ),
( 48, 0x3000, 0x303F ),
( 49, 0x3040, 0x309F ),
( 50, 0x30A0, 0x30FF ),
( 50, 0x31F0, 0x31FF ),
( 51, 0x3100, 0x312F ),
( 51, 0x31A0, 0x31BF ),
( 52, 0x3130, 0x318F ),
( 53, 0xA840, 0xA87F ),
( 54, 0x3200, 0x32FF ),
( 55, 0x3300, 0x33FF ),
( 56, 0xAC00, 0xD7AF ),
( 57, 0xD800, 0xDFFF ),
( 58, 0x10900, 0x1091F ),
( 59, 0x4E00, 0x9FFF ),
( 59, 0x2E80, 0x2EFF ),
( 59, 0x2F00, 0x2FDF ),
( 59, 0x2FF0, 0x2FFF ),
( 59, 0x3400, 0x4DBF ),
( 59, 0x20000, 0x2A6DF ),
( 59, 0x3190, 0x319F ),
( 60, 0xE000, 0xF8FF ),
( 61, 0x31C0, 0x31EF ),
( 61, 0xF900, 0xFAFF ),
( 61, 0x2F800, 0x2FA1F ),
( 62, 0xFB00, 0xFB4F ),
( 63, 0xFB50, 0xFDFF ),
( 64, 0xFE20, 0xFE2F ),
( 65, 0xFE10, 0xFE1F ),
( 65, 0xFE30, 0xFE4F ),
( 66, 0xFE50, 0xFE6F ),
( 67, 0xFE70, 0xFEFF ),
( 68, 0xFF00, 0xFFEF ),
( 69, 0xFFF0, 0xFFFF ),
( 70, 0x0F00, 0x0FFF ),
( 71, 0x0700, 0x074F ),
( 72, 0x0780, 0x07BF ),
( 73, 0x0D80, 0x0DFF ),
( 74, 0x1000, 0x109F ),
( 75, 0x1200, 0x137F ),
( 75, 0x1380, 0x139F ),
( 75, 0x2D80, 0x2DDF ),
( 76, 0x13A0, 0x13FF ),
( 77, 0x1400, 0x167F ),
( 78, 0x1680, 0x169F ),
( 79, 0x16A0, 0x16FF ),
( 80, 0x1780, 0x17FF ),
( 80, 0x19E0, 0x19FF ),
( 81, 0x1800, 0x18AF ),
( 82, 0x2800, 0x28FF ),
( 83, 0xA000, 0xA48F ),
( 83, 0xA490, 0xA4CF ),
( 84, 0x1700, 0x171F ),
( 84, 0x1720, 0x173F ),
( 84, 0x1740, 0x175F ),
( 84, 0x1760, 0x177F ),
( 85, 0x10300, 0x1032F ),
( 86, 0x10330, 0x1034F ),
( 87, 0x10400, 0x1044F ),
( 88, 0x1D000, 0x1D0FF ),
( 88, 0x1D100, 0x1D1FF ),
( 88, 0x1D200, 0x1D24F ),
( 89, 0x1D400, 0x1D7FF ),
( 90, 0xF0000, 0xFFFFD ),
( 90, 0x100000, 0x10FFFD ),
( 91, 0xFE00, 0xFE0F ),
( 91, 0xE0100, 0xE01EF ),
( 92, 0xE0000, 0xE007F ),
( 93, 0x1900, 0x194F ),
( 94, 0x1950, 0x197F ),
( 95, 0x1980, 0x19DF ),
( 96, 0x1A00, 0x1A1F ),
( 97, 0x2C00, 0x2C5F ),
( 98, 0x2D30, 0x2D7F ),
( 99, 0x4DC0, 0x4DFF ),
( 100, 0xA800, 0xA82F ),
( 101, 0x10000, 0x1007F ),
( 101, 0x10080, 0x100FF ),
( 101, 0x10100, 0x1013F ),
( 102, 0x10140, 0x1018F ),
( 103, 0x10380, 0x1039F ),
( 104, 0x103A0, 0x103DF ),
( 105, 0x10450, 0x1047F ),
( 106, 0x10480, 0x104AF ),
( 107, 0x10800, 0x1083F ),
( 108, 0x10A00, 0x10A5F ),
( 109, 0x1D300, 0x1D35F ),
( 110, 0x12000, 0x123FF ),
( 110, 0x12400, 0x1247F ),
( 111, 0x1D360, 0x1D37F ),
( 112, 0x1B80, 0x1BBF ),
( 113, 0x1C00, 0x1C4F ),
( 114, 0x1C50, 0x1C7F ),
( 115, 0xA880, 0xA8DF ),
( 116, 0xA900, 0xA92F ),
( 117, 0xA930, 0xA95F ),
( 118, 0xAA00, 0xAA5F ),
( 119, 0x10190, 0x101CF ),
( 120, 0x101D0, 0x101FF ),
( 121, 0x102A0, 0x102DF ),
( 121, 0x10280, 0x1029F ),
( 121, 0x10920, 0x1093F ),
( 122, 0x1F030, 0x1F09F ),
( 122, 0x1F000, 0x1F02F )
)
def ul_char_ranges(font):
char_ranges = [0, 0, 0, 0]
for char in font.chars:
range = next((range for range in CHAR_RANGES if range[1] <= char.code <= range[2]), None)
if range is not None:
char_ranges[range[0] >> 5] |= 1 << (range[0] & 0x1F)
if font.max_code >= 0x10000:
char_ranges[57 >> 5] |= 1 << (57 & 0x1F)
return char_ranges
# -- ul_code_pages --
def ul_code_pages(font):
space_index = next((index for index, char in enumerate(font.chars) if char.code == 0x20), len(font.chars))
ascii = int(len(font.chars) >= space_index + 0x5F and font.chars[space_index + 0x5E].code == 0x7E)
findf = lambda unicode: int(next((char for char in font.chars if char.code == unicode), None) is not None)
graph = findf(0x2524)
radic = findf(0x221A)
code_pages = [0, 0]
# conditions from FontForge
for char in font.chars:
unicode = char.code
if unicode == 0x00DE:
code_pages[0] |= (ascii) << 0 # 1252 Latin1
elif unicode == 0x255A:
code_pages[1] |= (ascii) << 30 # 850 WE/Latin1
code_pages[1] |= (ascii) << 31 # 437 US
elif unicode == 0x013D:
code_pages[0] |= (ascii) << 1 # 1250 Latin 2: Eastern Europe
code_pages[1] |= (ascii & graph) << 26 # 852 Latin 2
elif unicode == 0x0411:
code_pages[0] |= 1 << 2 # 1251 Cyrillic
code_pages[1] |= (findf(0x255C) & graph) << 17 # 866 MS-DOS Russian
code_pages[1] |= (findf(0x0405) & graph) << 25 # 855 IBM Cyrillic
elif unicode == 0x0386:
code_pages[0] |= 1 << 3 # 1253 Greek
code_pages[1] |= (findf(0x00BD) & graph) << 16 # 869 IBM Greek
code_pages[1] |= (graph & radic) << 28 # 737 Greek; former 437 G
elif unicode == 0x0130:
code_pages[0] |= (ascii) << 4 # 1254 Turkish
code_pages[1] |= (ascii & graph) << 24 # 857 IBM Turkish
elif unicode == 0x05D0:
code_pages[0] |= 1 << 5 # 1255 Hebrew
code_pages[1] |= (graph & radic) << 21 # 862 Hebrew
elif unicode == 0x0631:
code_pages[0] |= 1 << 6 # 1256 Arabic
code_pages[1] |= (radic) << 19 # 864 Arabic
code_pages[1] |= (graph) << 29 # 708 Arabic; ASMO 708
elif unicode == 0x0157:
code_pages[0] |= (ascii) << 7 # 1257 Windows Baltic
code_pages[1] |= (ascii & graph) << 27 # 775 MS-DOS Baltic
elif unicode == 0x20AB:
code_pages[0] |= 1 << 8 # 1258 Vietnamese
elif unicode == 0x0E45:
code_pages[0] |= 1 << 16 # 874 Thai
elif unicode == 0x30A8:
code_pages[0] |= 1 << 17 # 932 JIS/Japan
elif unicode == 0x3105:
code_pages[0] |= 1 << 18 # 936 Chinese: Simplified chars
elif unicode == 0x3131:
code_pages[0] |= 1 << 19 # 949 Korean Wansung
elif unicode == 0x592E:
code_pages[0] |= 1 << 20 # 950 Chinese: Traditional chars
elif unicode == 0xACF4:
code_pages[0] |= 1 << 21 # 1361 Korean Johab
elif unicode == 0x2030:
code_pages[0] |= (findf(0x2211) & ascii) << 29 # Macintosh Character Set (Roman)
elif unicode == 0x2665:
code_pages[0] |= (ascii) << 30 # OEM Character Set
elif unicode == 0x00C5:
code_pages[1] |= (ascii & graph & radic) << 18 # 865 MS-DOS Nordic
elif unicode == 0x00E9:
code_pages[1] |= (ascii & graph & radic) << 20 # 863 MS-DOS Canadian French
elif unicode == 0x00F5:
code_pages[1] |= (ascii & graph & radic) << 23 # 860 MS-DOS Portuguese
elif unicode == 0x00FE:
code_pages[1] |= (ascii & graph) << 22 # 861 MS-DOS Icelandic
elif 0xF000 <= unicode <= 0xF0FF:
code_pages[0] |= 1 << 31 # Symbol Character Set
return code_pages
# -- strong_rtl_flag --
RTL_RANGES = (
( 0x05BE, 0x05BE ),
( 0x05C0, 0x05C0 ),
( 0x05C3, 0x05C3 ),
( 0x05C6, 0x05C6 ),
( 0x05D0, 0x05EA ),
( 0x05EF, 0x05F4 ),
( 0x0608, 0x0608 ),
( 0x060B, 0x060B ),
( 0x060D, 0x060D ),
( 0x061B, 0x061C ),
( 0x061E, 0x064A ),
( 0x066D, 0x066F ),
( 0x0671, 0x06D5 ),
( 0x06E5, 0x06E6 ),
( 0x06EE, 0x06EF ),
( 0x06FA, 0x070D ),
( 0x070F, 0x0710 ),
( 0x0712, 0x072F ),
( 0x074D, 0x07A5 ),
( 0x07B1, 0x07B1 ),
( 0x07C0, 0x07EA ),
( 0x07F4, 0x07F5 ),
( 0x07FA, 0x07FA ),
( 0x07FE, 0x0815 ),
( 0x081A, 0x081A ),
( 0x0824, 0x0824 ),
( 0x0828, 0x0828 ),
( 0x0830, 0x083E ),
( 0x0840, 0x0858 ),
( 0x085E, 0x085E ),
( 0x0860, 0x086A ),
( 0x08A0, 0x08B4 ),
( 0x08B6, 0x08BD ),
( 0x200F, 0x200F ),
( 0x202B, 0x202B ),
( 0x202E, 0x202E ),
( 0xFB1D, 0xFB1D ),
( 0xFB1F, 0xFB28 ),
( 0xFB2A, 0xFB36 ),
( 0xFB38, 0xFB3C ),
( 0xFB3E, 0xFB3E ),
( 0xFB40, 0xFB41 ),
( 0xFB43, 0xFB44 ),
( 0xFB46, 0xFBC1 ),
( 0xFBD3, 0xFD3D ),
( 0xFD50, 0xFD8F ),
( 0xFD92, 0xFDC7 ),
( 0xFDF0, 0xFDFC ),
( 0xFE70, 0xFE74 ),
( 0xFE76, 0xFEFC ),
( 0x10800, 0x10FFF ),
( 0x1E800, 0x1EFFF ),
(-1, 0)
)
def contains_rtl(font):
index = 0
for char in font.chars:
while char.code > RTL_RANGES[index][1]:
index += 1
if RTL_RANGES[index][0] == -1:
break
if char.code >= RTL_RANGES[index][0]:
return True
return False
# -- post_mac_names --
POST_MAC_NAMES = (
b'.notdef',
b'.null',
b'nonmarkingreturn',
b'space',
b'exclam',
b'quotedbl',
b'numbersign',
b'dollar',
b'percent',
b'ampersand',
b'quotesingle',
b'parenleft',
b'parenright',
b'asterisk',
b'plus',
b'comma',
b'hyphen',
b'period',
b'slash',
b'zero',
b'one',
b'two',
b'three',
b'four',
b'five',
b'six',
b'seven',
b'eight',
b'nine',
b'colon',
b'semicolon',
b'less',
b'equal',
b'greater',
b'question',
b'at',
b'A',
b'B',
b'C',
b'D',
b'E',
b'F',
b'G',
b'H',
b'I',
b'J',
b'K',
b'L',
b'M',
b'N',
b'O',
b'P',
b'Q',
b'R',
b'S',
b'T',
b'U',
b'V',
b'W',
b'X',
b'Y',
b'Z',
b'bracketleft',
b'backslash',
b'bracketright',
b'asciicircum',
b'underscore',
b'grave',
b'a',
b'b',
b'c',
b'd',
b'e',
b'f',
b'g',
b'h',
b'i',
b'j',
b'k',
b'l',
b'm',
b'n',
b'o',
b'p',
b'q',
b'r',
b's',
b't',
b'u',
b'v',
b'w',
b'x',
b'y',
b'z',
b'braceleft',
b'bar',
b'braceright',
b'asciitilde',
b'Adieresis',
b'Aring',
b'Ccedilla',
b'Eacute',
b'Ntilde',
b'Odieresis',
b'Udieresis',
b'aacute',
b'agrave',
b'acircumflex',
b'adieresis',
b'atilde',
b'aring',
b'ccedilla',
b'eacute',
b'egrave',
b'ecircumflex',
b'edieresis',
b'iacute',
b'igrave',
b'icircumflex',
b'idieresis',
b'ntilde',
b'oacute',
b'ograve',
b'ocircumflex',
b'odieresis',
b'otilde',
b'uacute',
b'ugrave',
b'ucircumflex',
b'udieresis',
b'dagger',
b'degree',
b'cent',
b'sterling',
b'section',
b'bullet',
b'paragraph',
b'germandbls',
b'registered',
b'copyright',
b'trademark',
b'acute',
b'dieresis',
b'notequal',
b'AE',
b'Oslash',
b'infinity',
b'plusminus',
b'lessequal',
b'greaterequal',
b'yen',
b'mu',
b'partialdiff',
b'summation',
b'product',
b'pi',
b'integral',
b'ordfeminine',
b'ordmasculine',
b'Omega',
b'ae',
b'oslash',
b'questiondown',
b'exclamdown',
b'logicalnot',
b'radical',
b'florin',
b'approxequal',
b'Delta',
b'guillemotleft',
b'guillemotright',
b'ellipsis',
b'nonbreakingspace',
b'Agrave',
b'Atilde',
b'Otilde',
b'OE',
b'oe',
b'endash',
b'emdash',
b'quotedblleft',
b'quotedblright',
b'quoteleft',
b'quoteright',
b'divide',
b'lozenge',
b'ydieresis',
b'Ydieresis',
b'fraction',
b'currency',
b'guilsinglleft',
b'guilsinglright',
b'fi',
b'fl',
b'daggerdbl',
b'periodcentered',
b'quotesinglbase',
b'quotedblbase',
b'perthousand',
b'Acircumflex',
b'Ecircumflex',
b'Aacute',
b'Edieresis',
b'Egrave',
b'Iacute',
b'Icircumflex',
b'Idieresis',
b'Igrave',
b'Oacute',
b'Ocircumflex',
b'apple',
b'Ograve',
b'Uacute',
b'Ucircumflex',
b'Ugrave',
b'dotlessi',
b'circumflex',
b'tilde',
b'macron',
b'breve',
b'dotaccent',
b'ring',
b'cedilla',
b'hungarumlaut',
b'ogonek',
b'caron',
b'Lslash',
b'lslash',
b'Scaron',
b'scaron',
b'Zcaron',
b'zcaron',
b'brokenbar',
b'Eth',
b'eth',
b'Yacute',
b'yacute',
b'Thorn',
b'thorn',
b'minus',
b'multiply',
b'onesuperior',
b'twosuperior',
b'threesuperior',
b'onehalf',
b'onequarter',
b'threequarters',
b'franc',
b'Gbreve',
b'gbreve',
b'Idotaccent',
b'Scedilla',
b'scedilla',
b'Cacute',
b'cacute',
b'Ccaron',
b'ccaron',
b'dcroat'
)
def post_mac_names():
return list(POST_MAC_NAMES)
+245
View File
@@ -0,0 +1,245 @@
/*
Copyright (C) 2017-2019 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
'use strict';
const fnutil = require('./fnutil.js');
const fncli = require('./fncli.js');
const fnio = require('./fnio.js');
const bdf = require('./bdf.js');
// -- Params --
class Params extends fncli.Params {
constructor() {
super();
this.filter = false;
this.family = null;
this.output = null;
}
}
// -- Options --
const HELP = ('' +
'usage: ucstoany [-f] [-F FAMILY] [-o OUTPUT] INPUT REGISTRY ENCODING TABLE...\n' +
'Generate a BDF font subset.\n' +
'\n' +
' -f, --filter Discard characters with unicode FFFF; with registry ISO10646,\n' +
' encode the first 32 characters with their indexes; with other\n' +
' registries, encode all characters with indexes\n' +
' -F FAMILY output font family name (default = input)\n' +
' -o OUTPUT output file (default = stdout)\n' +
' TABLE text file, one hexadecimal unicode per line\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a BDF 2.1 font with unicode encoding.\n' +
'Unlike ucs2any, all TABLE-s form a single subset of the input font.\n');
const VERSION = 'ucstoany 1.55, Copyright (C) 2019 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE;
class Options extends fncli.Options {
constructor() {
super(['-F', '-o'], HELP, VERSION);
}
parse(name, value, params) {
switch (name) {
case '-f':
case '--filter':
params.filter = true;
break;
case '-F':
if (value.includes('-')) {
throw new Error('FAMILY may not contain "-"');
}
params.family = value;
break;
case '-o':
params.output = value;
break;
default:
this.fallback(name, params);
}
}
}
// -- Main --
function mainProgram(nonopt, parsed) {
if (nonopt.length < 4) {
throw new Error('invalid number of arguments, try --help');
}
const input = nonopt[0];
const registry = nonopt[1];
const encoding = nonopt[2];
let newCodes = [];
if (!registry.match(/^[A-Za-z][\w.:()]*$/) || !encoding.match(/^[\w.:()]+$/)) {
throw new Error('invalid registry or encoding');
}
// READ INPUT
let ifs = new fnio.InputFileStream(input);
try {
var oldFont = bdf.Font.read(ifs);
ifs.close();
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
// READ TABLES
nonopt.slice(3).forEach(name => {
ifs = new fnio.InputFileStream(name);
try {
ifs.readLines(line => {
newCodes.push(fnutil.parseHex('unicode', line));
});
ifs.close();
} catch (e) {
e.message = ifs.location() + e.message;
throw e;
}
});
if (newCodes.length === 0) {
throw new Error('no characters in the output font');
}
// CREATE GLYPHS
const newFont = new bdf.Font();
const charMap = [];
let index = 0;
let unstart = 0;
if (parsed.filter) {
unstart = (registry === 'ISO10646') ? 32 : bdf.CHARS_MAX;
}
// faster than Map() for <= 4K chars
oldFont.chars.forEach(char => (charMap[char.code] = char));
newCodes.forEach(code => {
let oldChar = charMap[code];
const uniFFFF = (oldChar == null);
if (code === 0xFFFF && parsed.filter) {
index++;
return;
}
if (uniFFFF) {
if (code !== 0xFFFF) {
throw new Error(`${input} does not contain U+${fnutil.unihex(code)}`);
}
if (oldFont.defaultCode !== -1) {
oldChar = charMap[oldFont.defaultCode];
} else {
oldChar = charMap[0xFFFD];
if (oldChar == null) {
throw new Error(`${input} does not contain U+FFFF, and no replacement found`);
}
}
}
const newChar = Object.assign(new bdf.Char(), oldChar);
newChar.code = index >= unstart ? code : index;
index++;
newChar.props = new bdf.Props();
oldChar.props.forEach((name, value) => newChar.props.set(name, value));
newChar.props.set('ENCODING', newChar.code);
newFont.chars.push(newChar);
if (uniFFFF) {
newChar.props.set('STARTCHAR', 'uniFFFF');
} else if (oldChar.code === oldFont.defaultCode || (oldChar.code === 0xFFFD && newFont.defaultCode === -1)) {
newFont.defaultCode = newChar.code;
}
});
// CREATE HEADER
let numProps;
const family = (parsed.family != null) ? parsed.family : oldFont.xlfd[bdf.XLFD.FAMILY_NAME];
oldFont.props.forEach((name, value) => {
switch (name) {
case 'FONT':
newFont.xlfd = oldFont.xlfd.slice();
newFont.xlfd[bdf.XLFD.FAMILY_NAME] = family;
newFont.xlfd[bdf.XLFD.CHARSET_REGISTRY] = registry;
newFont.xlfd[bdf.XLFD.CHARSET_ENCODING] = encoding;
value = newFont.xlfd.join('-');
break;
case 'STARTPROPERTIES':
numProps = fnutil.parseDec(name, value, 1);
break;
case 'FAMILY_NAME':
value = fnutil.quote(family);
break;
case 'CHARSET_REGISTRY':
value = fnutil.quote(registry);
break;
case 'CHARSET_ENCODING':
value = fnutil.quote(encoding);
break;
case 'DEFAULT_CHAR':
if (newFont.defaultCode !== -1) {
value = newFont.defaultCode;
} else {
numProps -= 1;
return;
}
break;
case 'ENDPROPERTIES':
if (newFont.defaultCode !== -1 && newFont.props.get('DEFAULT_CHAR') == null) {
newFont.props.set('DEFAULT_CHAR', newFont.defaultCode);
numProps += 1;
}
newFont.props.set('STARTPROPERTIES', numProps);
break;
case 'CHARS':
value = newFont.chars.length;
break;
}
newFont.props.set(name, value);
});
// COPY FIELDS
newFont.bbx = oldFont.bbx;
// WRITE OUTPUT
let ofs = new fnio.OutputFileStream(parsed.output);
try {
newFont.write(ofs);
ofs.close();
} catch (e) {
e.message = ofs.location() + e.message + ofs.destroy();
throw e;
}
}
if (require.main === module) {
fncli.start('ucstoany.js', new Options(), new Params(), mainProgram);
}
+188
View File
@@ -0,0 +1,188 @@
#
# Copyright (C) 2017-2020 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import re
import copy
import fnutil
import fncli
import fnio
import bdf
# -- Params --
class Params(fncli.Params):
def __init__(self):
fncli.Params.__init__(self)
self.filter_ffff = False
self.family_name = None
self.output_name = None
# -- Options --
HELP = ('' +
'usage: ucstoany [-f] [-F FAMILY] [-o OUTPUT] INPUT REGISTRY ENCODING TABLE...\n' +
'Generate a BDF font subset.\n' +
'\n' +
' -f, --filter Discard characters with unicode FFFF; with registry ISO10646,\n' +
' encode the first 32 characters with their indexes; with other\n' +
' registries, encode all characters with indexes\n' +
' -F FAMILY output font family name (default = input)\n' +
' -o OUTPUT output file (default = stdout)\n' +
' TABLE text file, one hexadecimal unicode per line\n' +
' --help display this help and exit\n' +
' --version display the program version and license, and exit\n' +
' --excstk display the exception stack on error\n' +
'\n' +
'The input must be a BDF 2.1 font with unicode encoding.\n' +
'Unlike ucs2any, all TABLE-s form a single subset of the input font.\n')
VERSION = 'ucstoany 1.62, Copyright (C) 2017-2020 Dimitar Toshkov Zhekov\n\n' + fnutil.GPL2PLUS_LICENSE
class Options(fncli.Options):
def __init__(self):
fncli.Options.__init__(self, ['-F', '-o'], HELP, VERSION)
def parse(self, name, value, params):
if name in ['-f', '--filter']:
params.filter_ffff = True
elif name == '-F':
params.family_name = bytes(value, 'ascii')
if '-' in value:
raise Exception('FAMILY may not contain "-"')
elif name == '-o':
params.output_name = value
else:
self.fallback(name, params)
# -- Main --
def main_program(nonopt, parsed):
# NON-OPTIONS
if len(nonopt) < 4:
raise Exception('invalid number of arguments, try --help')
input_name = nonopt[0]
registry = nonopt[1]
encoding = nonopt[2]
new_codes = []
if not re.fullmatch(r'[A-Za-z][\w.:()]*', registry) or not re.fullmatch(r'[\w.:()]+', encoding):
raise Exception('invalid registry or encoding')
# READ INPUT
old_font = fnio.read_file(input_name, bdf.Font.read)
# READ TABLES
def load_code(line):
new_codes.append(fnutil.parse_hex('unicode', line))
for table_name in nonopt[3:]:
fnio.read_file(table_name, lambda ifs: ifs.read_lines(load_code))
if not new_codes:
raise Exception('no characters in the output font')
# CREATE GLYPHS
new_font = bdf.Font()
charmap = {char.code:char for char in old_font.chars}
index = 0
unstart = 0
family = parsed.family_name if parsed.family_name is not None else old_font.xlfd[bdf.XLFD.FAMILY_NAME]
if parsed.filter_ffff:
unstart = 32 if registry == 'ISO10646' else bdf.CHARS_MAX
for code in new_codes:
if code == 0xFFFF and parsed.filter_ffff:
index += 1
continue
if code in charmap:
old_char = charmap[code]
uni_ffff = False
else:
uni_ffff = True
if code != 0xFFFF:
raise Exception('%s does not contain U+%04X' % (input, code))
if old_font.default_code != -1:
old_char = charmap[old_font.default_code]
elif 0xFFFD in charmap:
old_char = charmap[0xFFFD]
else:
raise Exception('%s does not contain U+FFFF, and no replacement found' % input)
new_char = copy.copy(old_char)
new_char.code = code if index >= unstart else index
index += 1
new_char.props = copy.copy(old_char.props)
new_char.props.set('ENCODING', new_char.code)
new_font.chars.append(new_char)
if uni_ffff:
new_char.props.set('STARTCHAR', b'uniFFFF')
elif old_char.code == old_font.default_code or (old_char.code == 0xFFFD and new_font.default_code == -1):
new_font.default_code = new_char.code
# CREATE HEADER
registry = bytes(registry, 'ascii')
encoding = bytes(encoding, 'ascii')
for [name, value] in old_font.props:
if name == 'FONT':
new_font.xlfd = old_font.xlfd[:]
new_font.xlfd[bdf.XLFD.FAMILY_NAME] = family
new_font.xlfd[bdf.XLFD.CHARSET_REGISTRY] = registry
new_font.xlfd[bdf.XLFD.CHARSET_ENCODING] = encoding
value = b'-'.join(new_font.xlfd)
elif name == 'STARTPROPERTIES':
num_props = fnutil.parse_dec(name, value, 1)
elif name == 'FAMILY_NAME':
value = fnutil.quote(family)
elif name == 'CHARSET_REGISTRY':
value = fnutil.quote(registry)
elif name == 'CHARSET_ENCODING':
value = fnutil.quote(encoding)
elif name == 'DEFAULT_CHAR':
if new_font.default_code != -1:
value = new_font.default_code
else:
num_props -= 1
continue
elif name == 'ENDPROPERTIES':
if new_font.default_code != -1 and new_font.props.get('DEFAULT_CHAR') is None:
new_font.props.set('DEFAULT_CHAR', new_font.default_code)
num_props += 1
new_font.props.set('STARTPROPERTIES', num_props)
elif name == 'CHARS':
value = len(new_font.chars)
new_font.props.set(name, value)
# COPY FIELDS
new_font.bbx = old_font.bbx
# WRITE OUTPUT
fnio.write_file(parsed.output_name, lambda ofs: new_font.write(ofs))
if __name__ == '__main__':
fncli.start('ucstoany.py', Options(), Params(), main_program)
+68
View File
@@ -0,0 +1,68 @@
#!/bin/sh
if test "$1" = "--help" ; then
echo usage: $0 [--var=VALUE] [VAR=VALUE] ...
echo
cat configure.help 2> /dev/null && echo
echo "Any variables not explicitly set are reset to their defaults"
elif test "$1" = "--version" ; then
cat << EOT
micro configure 0.21, Copyright (C) 2015 Dimitar Toshkov Zhekov
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
EOT
elif test -z `printf "\r"` ; then
echo "$0: printf failure"
false
elif test -f Makefile ; then
if test -f Makefile.in ; then
cp -fp Makefile.in Makefile
else
cp -p Makefile Makefile.in
fi
w=`printf "[\t ]*"`
r=`printf "\r"`
l="[a-z][a-z0-9-]*[^-]"
for i do
if test "$i" != "`echo "$i" | sed -e 1q`" ; then
echo "$0: `echo "$i" | sed -e "s/$r/^M/" -e 1q`^J...: contains line feed" 1>&2
elif echo "$i" | grep "$r" > /dev/null ; then
echo "$0: `echo "$i" | sed -e "s/$r.*//"`^M...: contains carriage return" 1>&2
elif test -n "$i" ; then
if echo "$i" | grep -E "^--$l=|^[A-Z][A-Z0-9_]*[^_]=" > /dev/null ; then
n=`echo "$i" | sed -e "s$r^-*$r$r" -e "s$r=.*$r$r" | sed -e "s/-/_/g"`
if grep -E "^$n$w:?=" Makefile > /dev/null ; then
cp -f Makefile Makefile.$$
sed -e "s$r^\($n$w:*=$w\).*$r\1`echo "$i" | sed -e "s$r^[^=]*=$r$r"`$r" Makefile.$$ > Makefile
grep -E "^$n$w:?=" Makefile /dev/null
else
echo "$0: $n: not found in Makefile" 1>&2
fi
unset n
else
echo "$0: $i: not recognized" 1>&2
fi
fi
done
unset i l r w
if test -f Makefile.$$ ; then
rm -f Makefile.$$
else
rm -f Makefile.in
fi
else
echo "$0: Makefile: not found" 1>&2
false
fi
+14
View File
@@ -0,0 +1,14 @@
To assign environment variables (e.g., CC, CFLAGS...), specify them as
NAME=VALUE. See README for a description of all options and targets.
The default values are specified in brackets.
Configuration:
--help display this help and exit
--version display version information and exit
Installation directories:
--prefix=PREFIX prefix for all directories [/usr/local]
--psfdir=DIR PC screen fonts directory [PREFIX/share/consolefonts]
--x11dir=DIR X11 fonts directory [PREFIX/share/fonts/terminus]
--otbdir=DIR TrueType/OTB fonts directory [PREFIX/share/fonts/terminus]
+2
View File
@@ -0,0 +1,2 @@
0020 0000
2302 007F
+5
View File
@@ -0,0 +1,5 @@
25B6 25BA
25C0 25C4
2666 25C6
266B 266C
FFFD 0000
+7
View File
@@ -0,0 +1,7 @@
00B5 03BC
03A3 2211
03A9 2126
03B2 00DF
220A 03B5
220A 2208
2205 03C6
+1
View File
@@ -0,0 +1 @@
0138 043A
+1
View File
@@ -0,0 +1 @@
006B 043A
+1
View File
@@ -0,0 +1 @@
043F 03C0
+8
View File
@@ -0,0 +1,8 @@
2191 25B2
2193 25BC
25B6 2192
25B6 25BA
25A0 FFFD
25C0 2190
25C0 25C4
2666 25C6
+107
View File
@@ -0,0 +1,107 @@
0020 0000
0020 00A0
0020 2000
0020 2001
0020 2002
0020 2003
0020 2004
0020 2005
0020 2006
0020 2007
0020 2008
0020 2009
0020 200A
0020 202F
002D 2012
002D 2013
002D 2212
0033 0417
0041 0391
0041 0410
0042 0392
0042 0412
0043 0421
0045 0395
0045 0415
0048 0397
0048 041D
0049 0399
0049 0406
0049 04C0
004A 0408
004B 039A
004B 041A
004D 039C
004D 041C
004E 039D
004F 039F
004F 041E
0050 03A1
0050 0420
0053 0405
0054 03A4
0054 0422
0058 03A7
0058 0425
0059 03A5
0059 04AE
005A 0396
0061 0430
0063 03F2
0063 0441
0065 0435
0068 210E
0069 0456
006A 03F3
006A 0458
006C 04CF
006F 03BF
006F 043E
0070 0440
0073 0455
0076 03BD
0078 0445
0079 0443
00AD 2010
00AD 2011
00B4 0301
00B5 03BC
00C4 04D2
00C6 04D4
00C8 0400
00CB 0401
00CF 03AA
00CF 0407
00D0 0110
00D6 04E6
00E4 04D3
00E6 04D5
00E8 0450
00EB 0451
00EF 0457
00F3 03CC
00F6 04E7
0102 04D0
0103 04D1
0138 03BA
0178 03AB
02C6 0302
02C7 030C
02D8 0306
02DC 0303
0393 0413
03A9 2126
03B5 2208
03B5 220A
03C0 043F
03C6 2205
041F 03A0
045B 0127
045B 210F
04AF 03B3
04D8 018F
04D9 0259
04E8 03F4
2014 2015
2206 0394
0138 043A
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

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