From 8d61e0f4e2d3a43cf0966f8b1714027a34c6bb14 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Thu, 21 Oct 2021 15:13:45 -0700 Subject: [PATCH 01/25] Selawik-variable.ttf test font --- tests/data/LICENSE.txt | 91 ++++++++++++++++++++++++++++++++ tests/data/Selawik-README.md | 60 +++++++++++++++++++++ tests/data/Selawik-variable.ttf | Bin 0 -> 481464 bytes 3 files changed, 151 insertions(+) create mode 100644 tests/data/LICENSE.txt create mode 100644 tests/data/Selawik-README.md create mode 100644 tests/data/Selawik-variable.ttf diff --git a/tests/data/LICENSE.txt b/tests/data/LICENSE.txt new file mode 100644 index 0000000..ac8fdb9 --- /dev/null +++ b/tests/data/LICENSE.txt @@ -0,0 +1,91 @@ +Copyright 2015, Microsoft Corporation (www.microsoft.com), with Reserved Font Name Selawik. All Rights Reserved. Selawik is a trademark of Microsoft Corporation in the United States and/or other countries. + +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. diff --git a/tests/data/Selawik-README.md b/tests/data/Selawik-README.md new file mode 100644 index 0000000..9c5a80f --- /dev/null +++ b/tests/data/Selawik-README.md @@ -0,0 +1,60 @@ +# Overview +This version of Selawik is a test case and demonstration of OpenType 1.8 Font Variations technology and tables. It also includes some handy debugging characters. + +This version of Selawik is intended for testing only, and is not recommended for shipping in applications, etc. For that, it is better to use the main branch of [Selawik](https://github.com/Microsoft/Selawik). + +# Features + +* Full TrueType hinting with VTT source tables included. See [Hinting](#hinting) for details. +* All tables required for OpenType 1.8 are present (see [Table Status](#table-status), below). This includes cvar (varied CVTs), GPOS/GDEF (kerning varies), and avar (coordinate space warping to match Segoe UI). + + Note: This version of Selawik does not include an MVAR because its vertical metrics do not change anywhere in the design space, thus there is no need for MVAR. A future release will contain an axis that varies vertical metrics as an excuse to have an MVAR. + +* Numerous interesting debugging glyphs (requires liga to be enabled). For example, \axis1 will show the current normalized wght coordinate. See [Debugging Glyphs](#debugging-glyphs) for details. +* 1 axis: weight + +## Table status +The following tables are currently supported: + +- [x] fvar +- [x] gvar +- [x] cvar +- [x] avar (to match Segoe UI weights and metrics) +- [x] STAT +- [x] GPOS/GDEF - kerning +- [x] HVAR + +Not yet complete: + +- [ ] GSUB/GDEF - to change dollar signs in the bold +- [ ] MVAR (future release) + +## To do +* Add a second axis that varies vertical metrics so we need an MVAR table. This axis will not be one of the standard axes listed in the [OpenType 1.8 specification] (https://www.microsoft.com/typography/otspec/fvar.htm), so that it becomes an example of out to do a foundry-defined axis. +* Add Feature Variations (GPOS/GDEF) to switch dollar sign glyphs across weights. + + +# Debugging glyphs +Thanks to Greg Hitchcock's TrueType coding wizardry, this font includes many glyphs that are helpful for debugging implementations of variable fonts. It has a number of substitutions implemented as liga features: + +Feature | Description +-------- | ---------- +\axis1 | Shows the normalized coordinate on the wght axis for the currently selected instance (e.g. 1.0 for bold, -1.0 for light, or something in between). +\axis2 | Shows 0 because this font doesn't yet have a second axis. +\axis1hex | Same as \axis1 but in hex. +\axis2hex | Same as \axis2 but in hex. +\pointsize | Shows the point size passed to the TrueType rasterizer. Note that depending on how the application calls the rasterizer, this may not be what you expect - e.g. on Safari on MacOS, this is always 1024. +\ppem | Shows the pixels per em passed to the TrueType rastersize. Note that depending on how the application calls the rasterizer, this may not be what you expect - e.g. on Safari on MacOS, this is always 1024. +\ttversion | Shows the version of the TrueType rasterizer. +\ttmode | Shows the current TrueType rasterizer mode flags. +\boldtest | A glyph to help you detect artificial emboldening. The glyph has a vertical bar and a circle. The vertical bar's weight varies with the weight of the rest of the font: it gets bolder at bolder weights, lighter at lighter weights. The circle changes weight (and size) in opposition to the rest of the font: lighter at bold weights and vice versa. Thus, if you use this character and see both the circle and bar look bold, you're not looking at a true bold instance, but an algorithmically emboldened one. +\family | Shows the family name of the font. +\version | Shows the version of this font + + + + +# Hinting +This version of Selawik is primarily hinted with the light Latin hinting style Microsoft recommends for variable Latin fonts. The VTT Light Latin autohinter was used to create the first round of hints, which were then reviewed and touched up. + +This hinting style only uses CVTs for vertical metrics anchors (ascender, descender, cap height, x-height, and baseline). While this makes for an easy job hinting a Latin font, it makes for a poor test case because Selawik doesn't vary vertical metrics with weight, thus doesn't vary CVTs, thus doesn't need a cvar. So, to make it more interesting, we added CVT-based stem hints to the lowercase only. This provided the need to vary CVTs and thus require a cvar. Note that this was only done for testing purposes. For variable fonts, Microsoft recommends the light hinting style using the `ResYDist()` command instead of a CVT-based stem hint. diff --git a/tests/data/Selawik-variable.ttf b/tests/data/Selawik-variable.ttf new file mode 100644 index 0000000000000000000000000000000000000000..db26a3f4f8b50b102f1e1e7f89f9ef770abb9cf9 GIT binary patch literal 481464 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

X=uM<>@6Z-PhA@X)p_12kxKPNpKBZR1(4s+# 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 literal 0 HcmV?d00001 From 9a6bdc04b21a7e7a5764eb7b6235f60875fa96a2 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 9 Nov 2021 18:14:08 -0800 Subject: [PATCH 02/25] Separate application class from entry point module. --- src/application.cpp | 522 ++++++++++++++++++++++++++ src/application.h | 42 +++ src/pch.h | 1 + src/vttcompile.cpp | 500 +----------------------- vttcompile/vttcompile.vcxproj | 1 + vttcompile/vttcompile.vcxproj.filters | 3 + 6 files changed, 572 insertions(+), 497 deletions(-) create mode 100644 src/application.cpp create mode 100644 src/application.h diff --git a/src/application.cpp b/src/application.cpp new file mode 100644 index 0000000..112dedf --- /dev/null +++ b/src/application.cpp @@ -0,0 +1,522 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define _CRT_SECURE_NO_DEPRECATE +#define _CRT_NON_CONFORMING_SWPRINTFS +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING + +#include + +#include "pch.h" +#include "application.h" + +Application::Application(void) +{ + this->mainFont[0] = L'\0'; + this->textFont[0] = L'\0'; + this->fileName[0] = L'\0'; +} + +bool Application::Create() +{ + //wchar_t errMsg[maxLineSize]; + + this->font = std::make_unique(); + if (this->font == nullptr) + return false; + + this->glyph = std::make_unique(); + if (this->glyph == nullptr) + return false; + + if (!this->font->Create()) + return false; + + this->glyf = std::make_unique(); + if (this->glyf == nullptr) + return false; + + this->prep = std::make_unique(); + if (this->prep == nullptr) + return false; + + this->talk = std::make_unique(); + if (this->talk == nullptr) + return false; + + this->fpgm = std::make_unique(); + if (this->fpgm == nullptr) + return false; + + this->cpgm = std::make_unique(); + if (this->cpgm == nullptr) + return false; + + return true; +} + +Application::~Application(void) +{ + +} + +bool Application::OpenFont(std::string fileName, wchar_t errMsg[]) { + auto file = std::make_unique(); + + this->charCode = this->glyphIndex = INVALID_GLYPH_INDEX; + + this->fileName = fileName; + file->OpenOld(this->fileName.c_str()); + if (file->Error()){ + swprintf(errMsg, L"OpenFont: File Not Found"); + return false; + } + + if (!this->font->Read(file.get(), this->glyph.get(), &this->platformID, &this->encodingID, errMsg)) + return false; + + file->Close(false); + + return true; +} + +bool Application::SaveFont(StripCommand strip, wchar_t errMsg[]) +{ + return this->SaveFont(this->fileName, strip, errMsg); +} + +bool Application::SaveFont(std::string fileN, StripCommand strip, wchar_t errMsg[]) +{ + auto file = std::make_unique(); + errMsg[0] = 0; + + if (!this->BuildFont(strip, errMsg)) + return false; + + file->OpenNew(fileN.c_str()); + if (file->Error()) + return false; + + if (!this->font->Write(file.get(), errMsg)) + file->Close(true); + + file->Close(true); + + return true; +} + +bool Application::GotoFont(wchar_t errMsg[]) { + int32_t errPos, errLen; + bool legacy = false; + + if (!this->font->GetCvt(this->cpgm.get(), errMsg)) return false; + this->font->TheCvt()->Compile(this->cpgm.get(), NULL, legacy, &errPos, &errLen, errMsg); + if (!this->font->GetPrep(this->prep.get(), errMsg)) return false; + if (!this->font->GetFpgm(this->fpgm.get(), errMsg)) return false; + + return true; // by now, ignoring the fact that a stripped font will not have any of the sources above +} // Application::GotoFont + +bool Application::GotoGlyph(int32_t code, bool isGlyphIndex) { + int32_t numGlyphs = this->font->NumberOfGlyphs(), glyphIndex, charCode; + wchar_t errMsg[maxLineSize]; + + if (isGlyphIndex) { + glyphIndex = (code + numGlyphs) % numGlyphs; // cyclical clipping + charCode = this->font->CharCodeOf(glyphIndex); + } + else { + charCode = code; + glyphIndex = this->font->GlyphIndexOf(code); + if (glyphIndex == INVALID_GLYPH_INDEX) + glyphIndex = 0; + } + if (this->glyphIndex != glyphIndex || this->charCode != charCode) { + + this->glyphIndex = glyphIndex; + this->charCode = charCode; + this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); + this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); + } + return true; // by now, ignoring the fact that a stripped font will not have any of the sources above +} // Application::GotoGlyph + +bool Application::CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]) +{ + bool legacy = false; + + bool done = TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacy, errPos, errLen, errMsg); + + return done; +} + +bool Application::CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]) +{ + bool done = false; + bool legacy = false; + bool variationCompositeGuard = true; + + int32_t glyphIndex, binSize; + unsigned char* binData; + wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; + TextBuffer* errBuf = NULL; + + binData = (unsigned char*)NewP(MAXBINSIZE); + done = binData != NULL; + + glyphIndex = 0; + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. + if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); + + if (done) { + errBuf = new TextBuffer(); + done = errBuf != NULL; + } + + if (done) { + if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacy, errPos, errLen, compErrMsg)) { + // Compile updates cvt binary autonomously + this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); + } + else { + swprintf(tempErrMsg, L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(*errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (this->font->IsVariationFont()) { + this->font->ReverseInterpolateCvarTuples(); + + // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. + auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); + for (auto& tuple : *tuples) + { + // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values + // where the the edited value could have been optomized to another tuple. + bool edited = false; + for (auto& editedValue : tuple->editedCvtValues) + { + edited |= editedValue.Edited(); + } + + // No data and not edits then not needed in cvar + if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied + { + tuple->SetAsCvar(false); + } + } + } + + if (done) { + if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) + done = this->font->UpdateBinData(asmFPGM, binSize, binData); + else { + done = this->font->UpdateBinData(asmFPGM, 0, NULL); + swprintf(tempErrMsg, L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(*errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) { + if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) + done = this->font->UpdateBinData(asmPREP, binSize, binData); + else { + done = this->font->UpdateBinData(asmPREP, 0, NULL); + swprintf(tempErrMsg, L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(*errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (binData != NULL) DisposeP((void**)&binData); + + done = done && errBuf->Length() == 0; + + if (!done) { + + if (errBuf->Length() > 0) { + std::wstring errStr; + + errBuf->GetText(errStr); + + fwprintf(stderr, errStr.c_str()); + fwprintf(stderr, L"\n"); + } + } + + if (errBuf != NULL) delete errBuf; + + return done; +} + +bool Application::CompileGlyphRange(unsigned short g1, unsigned short g2, bool quiet, wchar_t errMsg[]) +{ + int32_t glyphIndex, binSize, fromGlyph, toGlyph, numGlyphs = this->font->NumberOfGlyphs(); + bool done = false; + unsigned char* binData; + wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; + TextBuffer* errBuf = NULL; + bool legacyCompile = false; + bool variationCompositeGuard = true; + int32_t errPos = 0; + int32_t errLen = 0; + + binData = (unsigned char*)NewP(MAXBINSIZE); + done = binData != NULL; + + glyphIndex = 0; + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + + done = this->CompileCommon(&errPos, &errLen, errMsg); + if (!done) + return done; + + if (done) { + errBuf = new TextBuffer(); + done = errBuf != NULL; + } + + fromGlyph = g1; + toGlyph = g2; + for (glyphIndex = fromGlyph; glyphIndex <= toGlyph && glyphIndex < numGlyphs && done; glyphIndex++) { + if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); + if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); + + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); + + if (done) { + if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, &errPos, &errLen, compErrMsg)) { + swprintf(tempErrMsg, L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + swprintf(tempErrMsg, L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(errPos), compErrMsg); + this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors + } + } + + binSize = 0; + if (done) { + if (TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) { + done = this->font->UpdateBinData(asmGLYF, binSize, binData); + } + else + { + done = this->font->UpdateBinData(asmGLYF, 0, NULL); + swprintf(tempErrMsg, L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) + { + done = this->BuildFont(stripNothing, compErrMsg); + } + } + if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); + + if (binData != NULL) DisposeP((void**)&binData); + + done = done && errBuf->Length() == 0; + + if (!done) { + if (errBuf->Length() > 0) { + std::wstring errStr; + + errBuf->GetText(errStr); + + fwprintf(stderr, errStr.c_str()); + fwprintf(stderr, L"\n"); + } + } + + if (errBuf != NULL) delete errBuf; + + return done; +} + +bool Application::CompileAll(bool quiet, wchar_t errMsg[]) +{ + int32_t glyphIndex, binSize, fromGlyph, fromChar, numGlyphs = this->font->NumberOfGlyphs(); + bool done; + unsigned char* binData; + wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; + TextBuffer* errBuf = NULL; + bool legacyCompile = false; + bool variationCompositeGuard = true; + int32_t errPos = 0; + int32_t errLen = 0; + + binData = (unsigned char*)NewP(MAXBINSIZE); + done = binData != NULL; + + glyphIndex = 0; + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. + if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); + + if (done) { + done = this->font->InitIncrBuildSfnt(false, errMsg); + } + + if (done) { + errBuf = new TextBuffer(); + done = errBuf != NULL; + } + + if (done) { + if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacyCompile, &errPos, &errLen, compErrMsg)) { + // Compile updates cvt binary autonomously + this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); + } + else { + swprintf(tempErrMsg, L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + + if (this->font->IsVariationFont()) { + this->font->ReverseInterpolateCvarTuples(); + + // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. + auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); + for (auto& tuple : *tuples) + { + // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values + // where the the edited value could have been optomized to another tuple. + bool edited = false; + for (auto& editedValue : tuple->editedCvtValues) + { + edited |= editedValue.Edited(); + } + + // No data and not edits then not needed in cvar + if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied + { + tuple->SetAsCvar(false); + } + } + } + + if (done) { + if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) + done = this->font->UpdateBinData(asmFPGM, binSize, binData); + else { + done = this->font->UpdateBinData(asmFPGM, 0, NULL); + swprintf(tempErrMsg, L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) { + if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) + done = this->font->UpdateBinData(asmPREP, binSize, binData); + else { + done = this->font->UpdateBinData(asmPREP, 0, NULL); + swprintf(tempErrMsg, L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + fromGlyph = this->glyphIndex; fromChar = this->charCode; + for (glyphIndex = 0; glyphIndex < numGlyphs && done; glyphIndex++) { + if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); + if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); + //this->MakeProgress(glyphIndex, glyphIndex); + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); + + if (done) { + if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, &errPos, &errLen, compErrMsg)) { + swprintf(tempErrMsg, L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + swprintf(tempErrMsg, L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(errPos), compErrMsg); + this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors + } + } + + binSize = 0; + if (done) { + if (!TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) { + swprintf(tempErrMsg, L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) done = this->font->AddGlyphToNewSfnt(this->font->CharGroupOf(glyphIndex), glyphIndex, this->glyph.get(), binSize, binData, this->glyf.get(), this->talk.get(), errMsg); + } + if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); + + done = this->font->TermIncrBuildSfnt(!done, this->prep.get(), this->cpgm.get(), this->fpgm.get(), errMsg); + + if (binData != NULL) DisposeP((void**)&binData); + + done = done && errBuf->Length() == 0; + + if (!done) { + // this error is supposed to report the reason that precluded further execution + //if (STRLENW(errMsg) > 0) + // ErrorBox(errMsg); + // these errors are supposed to report failed compilations (give the user a chance to fix the code) + if (errBuf->Length() > 0) { + std::wstring errStr; + errBuf->GetText(errStr); + swprintf(errMsg, L"Compile Error: " WIDE_STR_FORMAT, errStr.c_str()); + } + } + + if (errBuf != NULL) delete errBuf; + + return done; +} + +bool Application::BuildFont(StripCommand strip, wchar_t errMsg[]) { + + // If we did not compile and are just here to strip data then perform lazy initialization. + if (this->glyphIndex == INVALID_GLYPH_INDEX) + { + this->glyphIndex = 0; + this->charCode = this->font->CharCodeOf(this->glyphIndex); + } + + // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. + bool done = true; + if (done) done = this->font->GetGlyph(this->glyphIndex, this->glyph.get(), errMsg); + if (done) done = this->font->GetTalk(this->glyphIndex, this->talk.get(), errMsg); + if (done) done = this->font->GetGlyf(this->glyphIndex, this->glyf.get(), errMsg); + + return this->font->BuildNewSfnt(strip, anyGroup, this->glyphIndex, this->glyph.get(), this->glyf.get(), + this->prep.get(), this->cpgm.get(), this->talk.get(), this->fpgm.get(), errMsg); +} + +char* Application::wCharToChar(char out[], const wchar_t in[]) +{ + std::string text8; + + out[0] = '\0'; // just in case + + std::wstring_convert> converter; + text8 = converter.to_bytes(in); + + size_t len = static_cast(text8.size()); + if (len > 0) + { + std::copy(text8.begin(), text8.end(), out); + out[text8.size()] = '\0'; // don't forget the terminating 0 + } + + return out; +} \ No newline at end of file diff --git a/src/application.h b/src/application.h new file mode 100644 index 0000000..cea93a7 --- /dev/null +++ b/src/application.h @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +class Application +{ +public: + Application(void); + bool Create(); + virtual ~Application(void); + + bool OpenFont(std::string fileName, wchar_t errMsg[]); + bool SaveFont(StripCommand strip, wchar_t errMsg[]); + bool SaveFont(std::string fileName, StripCommand strip, wchar_t errMsg[]); + + bool GotoFont(wchar_t errMsg[]); + bool GotoGlyph(int32_t code, bool isGlyphIndex); + + bool CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]); + bool CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]); + + bool CompileGlyphRange(unsigned short g1, unsigned short g2, bool quiet, wchar_t errMsg[]); + bool CompileAll(bool quiet, wchar_t errMsg[]); + + bool BuildFont(StripCommand strip, wchar_t errMsg[]); + + char* wCharToChar(char out[], const wchar_t in[]); + +private: + + std::unique_ptr glyf = nullptr, prep = nullptr, talk = nullptr, fpgm = nullptr, cpgm = nullptr; + short platformID = 3, encodingID = 1; + + wchar_t mainFont[maxLineSize], textFont[maxLineSize]; + + bool fontOpen = false; + std::unique_ptr font = nullptr; + std::unique_ptr glyph = nullptr; + std::string fileName; + int32_t charCode = 0, glyphIndex = 0; +}; + diff --git a/src/pch.h b/src/pch.h index 8a76e0e..b6af5d9 100644 --- a/src/pch.h +++ b/src/pch.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "opentypedefs.h" diff --git a/src/vttcompile.cpp b/src/vttcompile.cpp index 8218f57..0db42c6 100644 --- a/src/vttcompile.cpp +++ b/src/vttcompile.cpp @@ -1,506 +1,16 @@ - // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. - #define _CRT_SECURE_NO_DEPRECATE #define _CRT_NON_CONFORMING_SWPRINTFS #include #include "pch.h" -#include "vttcompile.h" +#include "application.h" #include "getopt.h" -Application::Application(void) -{ - this->mainFont[0] = L'\0'; - this->textFont[0] = L'\0'; - this->fileName[0] = L'\0'; -} - -bool Application::Create() -{ - //wchar_t errMsg[maxLineSize]; - - this->font = std::make_unique(); - if (this->font == nullptr) - return false; - - this->glyph = std::make_unique(); - if (this->glyph == nullptr) - return false; - - if (!this->font->Create()) - return false; - - this->glyf = std::make_unique(); - if (this->glyf == nullptr) - return false; - - this->prep = std::make_unique(); - if (this->prep == nullptr) - return false; - - this->talk = std::make_unique(); - if (this->talk == nullptr) - return false; - - this->fpgm = std::make_unique(); - if (this->fpgm == nullptr) - return false; - - this->cpgm = std::make_unique(); - if (this->cpgm == nullptr) - return false; - - return true; -} - -Application::~Application(void) -{ - -} - -bool Application::OpenFont(std::string fileName, wchar_t errMsg[]) { - auto file = std::make_unique(); - - this->charCode = this->glyphIndex = INVALID_GLYPH_INDEX; - - this->fileName = fileName; - file->OpenOld(this->fileName.c_str()); - if (file->Error()) - return false; - - if (!this->font->Read(file.get(), this->glyph.get(), &this->platformID, &this->encodingID, errMsg)) - return false; - - file->Close(false); - - return true; -} - -bool Application::SaveFont(StripCommand strip, wchar_t errMsg[]) -{ - return this->SaveFont(this->fileName, strip, errMsg); -} - -bool Application::SaveFont(std::string fileN, StripCommand strip, wchar_t errMsg[]) -{ - auto file = std::make_unique(); - errMsg[0] = 0; - - if (!this->BuildFont(strip, errMsg)) - return false; - - file->OpenNew(fileN.c_str()); - if (file->Error()) - return false; - - if (!this->font->Write(file.get(), errMsg)) - file->Close(true); - - file->Close(true); - - return true; -} - -bool Application::GotoFont(wchar_t errMsg[]) { - int32_t errPos, errLen; - bool legacy = false; - - if (!this->font->GetCvt(this->cpgm.get(), errMsg)) return false; - this->font->TheCvt()->Compile(this->cpgm.get(), NULL, legacy, &errPos, &errLen, errMsg); - if (!this->font->GetPrep(this->prep.get(), errMsg)) return false; - if (!this->font->GetFpgm(this->fpgm.get(), errMsg)) return false; - - return true; // by now, ignoring the fact that a stripped font will not have any of the sources above -} // Application::GotoFont - -bool Application::GotoGlyph(int32_t code, bool isGlyphIndex) { - int32_t numGlyphs = this->font->NumberOfGlyphs(), glyphIndex, charCode; - wchar_t errMsg[maxLineSize]; - - if (isGlyphIndex) { - glyphIndex = (code + numGlyphs) % numGlyphs; // cyclical clipping - charCode = this->font->CharCodeOf(glyphIndex); - } - else { - charCode = code; - glyphIndex = this->font->GlyphIndexOf(code); - if (glyphIndex == INVALID_GLYPH_INDEX) glyphIndex = 0; - } - if (this->glyphIndex != glyphIndex || this->charCode != charCode) { - - this->glyphIndex = glyphIndex; - this->charCode = charCode; - this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - } - return true; // by now, ignoring the fact that a stripped font will not have any of the sources above -} // Application::GotoGlyph - -bool Application::CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]) -{ - bool legacy = false; - - bool done = TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacy, errPos, errLen, errMsg); - - return done; -} - -bool Application::CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]) -{ - bool done = false; - bool legacy = false; - bool variationCompositeGuard = true; - - int32_t glyphIndex, binSize; - unsigned char* binData; - wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; - TextBuffer* errBuf = NULL; - - binData = (unsigned char*)NewP(MAXBINSIZE); - done = binData != NULL; - - glyphIndex = 0; - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. - if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - errBuf = new TextBuffer(); - done = errBuf != NULL; - } - - if (done) { - if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacy, errPos, errLen, compErrMsg)) { - // Compile updates cvt binary autonomously - this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); - } - else { - swprintf(tempErrMsg, L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (this->font->IsVariationFont()) { - this->font->ReverseInterpolateCvarTuples(); - - // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. - auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); - for (auto& tuple : *tuples) - { - // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values - // where the the edited value could have been optomized to another tuple. - bool edited = false; - for (auto& editedValue : tuple->editedCvtValues) - { - edited |= editedValue.Edited(); - } - - // No data and not edits then not needed in cvar - if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied - { - tuple->SetAsCvar(false); - } - } - } - - if (done) { - if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) - done = this->font->UpdateBinData(asmFPGM, binSize, binData); - else { - done = this->font->UpdateBinData(asmFPGM, 0, NULL); - swprintf(tempErrMsg, L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) { - if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) - done = this->font->UpdateBinData(asmPREP, binSize, binData); - else { - done = this->font->UpdateBinData(asmPREP, 0, NULL); - swprintf(tempErrMsg, L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (binData != NULL) DisposeP((void**)&binData); - - done = done && errBuf->Length() == 0; - - if (!done) { - - if (errBuf->Length() > 0) { - std::wstring errStr; - - errBuf->GetText(errStr); - - fwprintf(stderr, errStr.c_str()); - fwprintf(stderr, L"\n"); - } - } - - if (errBuf != NULL) delete errBuf; - - return done; -} - -bool Application::CompileGlyphRange(unsigned short g1, unsigned short g2, int32_t* errPos, int32_t* errLen, bool quiet, wchar_t errMsg[]) -{ - int32_t glyphIndex, binSize, fromGlyph, toGlyph, numGlyphs = this->font->NumberOfGlyphs(); - bool done = false; - unsigned char* binData; - wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; - TextBuffer* errBuf = NULL; - bool legacyCompile = false; - bool variationCompositeGuard = true; - - binData = (unsigned char*)NewP(MAXBINSIZE); - done = binData != NULL; - - glyphIndex = 0; - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - - done = this->CompileCommon(errPos, errLen, errMsg); - if (!done) - return done; - - if (done) { - errBuf = new TextBuffer(); - done = errBuf != NULL; - } - - fromGlyph = g1; - toGlyph = g2; - for (glyphIndex = fromGlyph; glyphIndex <= toGlyph && glyphIndex < numGlyphs && done; glyphIndex++) { - if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); - if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); - - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, errPos, errLen, compErrMsg)) { - swprintf(tempErrMsg, L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - swprintf(tempErrMsg, L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(*errPos), compErrMsg); - this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors - } - } - - binSize = 0; - if (done) { - if (TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) { - done = this->font->UpdateBinData(asmGLYF, binSize, binData); - } - else - { - done = this->font->UpdateBinData(asmGLYF, 0, NULL); - swprintf(tempErrMsg, L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) - { - done = this->BuildFont(stripNothing, compErrMsg); - } - } - if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); - - if (binData != NULL) DisposeP((void**)&binData); - - done = done && errBuf->Length() == 0; - - if (!done) { - if (errBuf->Length() > 0) { - std::wstring errStr; - - errBuf->GetText(errStr); - - fwprintf(stderr, errStr.c_str()); - fwprintf(stderr, L"\n"); - } - } - - if (errBuf != NULL) delete errBuf; - - return done; -} - -bool Application::CompileAll(int32_t* errPos, int32_t* errLen, bool quiet, wchar_t errMsg[]) -{ - int32_t glyphIndex, binSize, fromGlyph, fromChar, numGlyphs = this->font->NumberOfGlyphs(); - bool done; - unsigned char* binData; - wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; - TextBuffer* errBuf = NULL; - bool legacyCompile = false; - bool variationCompositeGuard = true; - - binData = (unsigned char*)NewP(MAXBINSIZE); - done = binData != NULL; - - glyphIndex = 0; - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. - if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - done = this->font->InitIncrBuildSfnt(false, errMsg); - } - - if (done) { - errBuf = new TextBuffer(); - done = errBuf != NULL; - } - - if (done) { - if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacyCompile, errPos, errLen, compErrMsg)) { - // Compile updates cvt binary autonomously - this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); - } - else { - swprintf(tempErrMsg, L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - - if (this->font->IsVariationFont()) { - this->font->ReverseInterpolateCvarTuples(); - - // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. - auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); - for (auto& tuple : *tuples) - { - // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values - // where the the edited value could have been optomized to another tuple. - bool edited = false; - for (auto& editedValue : tuple->editedCvtValues) - { - edited |= editedValue.Edited(); - } - - // No data and not edits then not needed in cvar - if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied - { - tuple->SetAsCvar(false); - } - } - } - - if (done) { - if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) - done = this->font->UpdateBinData(asmFPGM, binSize, binData); - else { - done = this->font->UpdateBinData(asmFPGM, 0, NULL); - swprintf(tempErrMsg, L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) { - if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) - done = this->font->UpdateBinData(asmPREP, binSize, binData); - else { - done = this->font->UpdateBinData(asmPREP, 0, NULL); - swprintf(tempErrMsg, L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - fromGlyph = this->glyphIndex; fromChar = this->charCode; - for (glyphIndex = 0; glyphIndex < numGlyphs && done; glyphIndex++) { - if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); - if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); - //this->MakeProgress(glyphIndex, glyphIndex); - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, errPos, errLen, compErrMsg)) { - swprintf(tempErrMsg, L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - swprintf(tempErrMsg, L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(*errPos), compErrMsg); - this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors - } - } - - binSize = 0; - if (done) { - if (!TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) { - swprintf(tempErrMsg, L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) done = this->font->AddGlyphToNewSfnt(this->font->CharGroupOf(glyphIndex), glyphIndex, this->glyph.get(), binSize, binData, this->glyf.get(), this->talk.get(), errMsg); - } - if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); - - done = this->font->TermIncrBuildSfnt(!done, this->prep.get(), this->cpgm.get(), this->fpgm.get(), errMsg); - - if (binData != NULL) DisposeP((void**)&binData); - - done = done && errBuf->Length() == 0; - - if (!done) { - // this error is supposed to report the reason that precluded further execution - //if (STRLENW(errMsg) > 0) - // ErrorBox(errMsg); - // these errors are supposed to report failed compilations (give the user a chance to fix the code) - if (errBuf->Length() > 0) { - std::wstring errStr; - - errBuf->GetText(errStr); - - fwprintf(stderr, errStr.c_str()); - fwprintf(stderr, L"\n"); - } - } - - if (errBuf != NULL) delete errBuf; - - return done; -} - -bool Application::BuildFont(StripCommand strip, wchar_t errMsg[]) { - - // If we did not compile and are just here to strip data then perform lazy initialization. - if (this->glyphIndex == INVALID_GLYPH_INDEX) - { - this->glyphIndex = 0; - this->charCode = this->font->CharCodeOf(this->glyphIndex); - } - - // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. - bool done = true; - if (done) done = this->font->GetGlyph(this->glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(this->glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(this->glyphIndex, this->glyf.get(), errMsg); - - return this->font->BuildNewSfnt(strip, anyGroup, this->glyphIndex, this->glyph.get(), this->glyf.get(), - this->prep.get(), this->cpgm.get(), this->talk.get(), this->fpgm.get(), errMsg); -} - int ShowUsage(wchar_t* strErr) { if (strErr) @@ -664,9 +174,7 @@ int main(int argc, char* argv[]) if (bCompileAll) { - int32_t errPos, errLen; - - if (!application->CompileAll(&errPos, &errLen, bQuiet, errMsg)) + if (!application->CompileAll(bQuiet, errMsg)) { fwprintf(stderr, errMsg); fwprintf(stderr, L"\n"); @@ -677,9 +185,7 @@ int main(int argc, char* argv[]) } else if (haveGlyph) { - int32_t errPos, errLen; - - if (!application->CompileGlyphRange((unsigned short)g1, (unsigned short)g2, &errPos, &errLen, bQuiet, errMsg)) + if (!application->CompileGlyphRange((unsigned short)g1, (unsigned short)g2, bQuiet, errMsg)) { fwprintf(stderr, errMsg); fwprintf(stderr, L"\n"); diff --git a/vttcompile/vttcompile.vcxproj b/vttcompile/vttcompile.vcxproj index ca86be3..1c65767 100644 --- a/vttcompile/vttcompile.vcxproj +++ b/vttcompile/vttcompile.vcxproj @@ -143,6 +143,7 @@ + diff --git a/vttcompile/vttcompile.vcxproj.filters b/vttcompile/vttcompile.vcxproj.filters index f719288..f281670 100644 --- a/vttcompile/vttcompile.vcxproj.filters +++ b/vttcompile/vttcompile.vcxproj.filters @@ -63,6 +63,9 @@ Source Files + + Source Files + From 2645c9e978ab045b62292d8ba44235170df331f7 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 9 Nov 2021 18:19:46 -0800 Subject: [PATCH 03/25] Add application.cpp to Makefile and CMakeLists.txt --- src/Makefile | 2 +- vttcompile/CMakeLists.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index b441364..c2a9512 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ OBJS = \ CvtManager.o File.o List.o MathUtils.o Memory.o Platform.o TextBuffer.o \ TMTParser.o TTAssembler.o TTEngine.o TTFont.o TTGenerator.o Variation.o \ -VariationInstance.o VariationModels.o vttcompile.o +VariationInstance.o VariationModels.o vttcompile.o application.o .cpp.o : diff --git a/vttcompile/CMakeLists.txt b/vttcompile/CMakeLists.txt index dc7fb6f..c96e2c6 100644 --- a/vttcompile/CMakeLists.txt +++ b/vttcompile/CMakeLists.txt @@ -51,6 +51,7 @@ set(Header_Files "../src/VariationInstance.h" "../src/VariationModels.h" "../src/vttcompile.h" + "../src/application.h" ) source_group("Header Files" FILES ${Header_Files}) @@ -71,6 +72,7 @@ set(Source_Files "../src/VariationInstance.cpp" "../src/VariationModels.cpp" "../src/vttcompile.cpp" + "../src/application.cpp" ) source_group("Source Files" FILES ${Source_Files}) From 0f03793d7f6e348a8bd09f483209897b537ba63d Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Mon, 15 Nov 2021 14:02:38 -0800 Subject: [PATCH 04/25] Initial Cython bindings. --- pypi.md | 37 ++++++++++++++++ pyproject.toml | 7 ++++ requirements-dev.txt | 6 +++ setup.cfg | 6 +++ setup.py | 62 +++++++++++++++++++++++++++ tests/test_vttcompile.py | 9 ++++ vttcompilepy/__init__.py | 1 + vttcompilepy/__main__.py | 52 +++++++++++++++++++++++ vttcompilepy/cvttcompilepy.pxd | 31 ++++++++++++++ vttcompilepy/vttcompilepy.pyx | 77 ++++++++++++++++++++++++++++++++++ 10 files changed, 288 insertions(+) create mode 100644 pypi.md create mode 100644 pyproject.toml create mode 100644 requirements-dev.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tests/test_vttcompile.py create mode 100644 vttcompilepy/__init__.py create mode 100644 vttcompilepy/__main__.py create mode 100644 vttcompilepy/cvttcompilepy.pxd create mode 100644 vttcompilepy/vttcompilepy.pyx diff --git a/pypi.md b/pypi.md new file mode 100644 index 0000000..cbe954d --- /dev/null +++ b/pypi.md @@ -0,0 +1,37 @@ +# Project + +VTTCompilePy is a Python extension built using Cython. It provides streamlined bindings for +various compilers from Visual TrueType. + +DWriteShapePy was developed to support a Python based font development environment. + +### Example + +```python + import sys + from pathlib import Path + import vttcompilepy as vtt + + TESTDATA = Path(__file__).parent / "data" + IN_PATH = TESTDATA / "selawik-variable.ttf" + OUT_PATH = TESTDATA / "out.ttf" + + print(bytes(IN_PATH)) + + print('VTTCompilePy Test Client') + + compiler = vtt.Compiler(IN_PATH) + + compiler.compile_all() + + compiler.save_font(OUT_PATH, vtt.StripLevel.STRIP_NOTHING) + +``` + +## Trademarks + +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft +trademarks or logos is subject to and must follow +[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). +Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. +Any use of third-party trademarks or logos are subject to those third-party's policies. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b0bbb69 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = [ + "setuptools", + "wheel", + "cython", +] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..71a0ec9 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,6 @@ +cython>=0.29.1 + +wheel>=0.31 + +pytest + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..612d313 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,6 @@ +[sdist] +formats = zip + +[metadata] +license_files = + LICENSE.txt diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..975bc07 --- /dev/null +++ b/setup.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +from io import open +import os +import sys +import platform +from setuptools import Extension, setup +from Cython.Build import cythonize + +here = os.path.abspath(os.path.dirname(__file__)) + +# Get the long description from the pypi.md file +with open(os.path.join(here, 'pypi.md'), encoding='utf-8') as f: + long_description = f.read() + +define_macros = [('UNICODE', 1), ('_UNICODE', 1)] +linetrace = False +if int(os.environ.get('CYTHON_LINETRACE', '0')): + linetrace = True + define_macros.append(('CYTHON_TRACE_NOGIL', '1')) + +extra_compile_args = ['/Zc:wchar_t'] + +if platform.system() != 'Windows': + extra_compile_args.append('-std=c++11') + +extension = Extension( + 'vttcompilepy.vttcompilepy', + define_macros=define_macros, + include_dirs=[".","src"], + sources=['vttcompilepy/vttcompilepy.pyx','src/application.cpp','src/CvtManager.cpp','src/File.cpp','src/List.cpp', 'src/mathutils.cpp', 'src/Memory.cpp','src/Platform.cpp','src/TextBuffer.cpp', 'src/TMTParser.cpp', 'src/TTAssembler.cpp', 'src/TTEngine.cpp', 'src/TTFont.cpp', 'src/TTGenerator.cpp', 'src/Variation.cpp', 'src/VariationInstance.cpp', 'src/VariationModels.cpp' ], + language='c++', + extra_compile_args=extra_compile_args, +) + +setup( + name="vttcompilepy", + version= '0.0.1.1b1', + description="Python extension for Visual TrueType font compile. ", + long_description=long_description, + long_description_content_type='text/markdown', + author="Paul Linnerud", + author_email="paulli@microsoft.com", + url="https://github.com/microsoft/VisualTrueType", + license="MIT", + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: Microsoft :: Windows :: Windows 10", + ], + package_dir={"": "."}, + packages=["vttcompilepy"], + zip_safe=False, + python_requires=">=3.7", + ext_modules = cythonize( + extension, + annotate=bool(int(os.environ.get('CYTHON_ANNOTATE', '0'))), + compiler_directives={"linetrace": linetrace}, + ), + entry_points={ + 'console_scripts': [ "vttcompilepy = vttcompilepy.__main__:main" ] + } +) diff --git a/tests/test_vttcompile.py b/tests/test_vttcompile.py new file mode 100644 index 0000000..e295fc4 --- /dev/null +++ b/tests/test_vttcompile.py @@ -0,0 +1,9 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +from pathlib import Path +import pytest + +TESTDATA = Path(__file__).parent / "data" +SELAWIK_PATH = TESTDATA / "Selawik-variable.ttf" + diff --git a/vttcompilepy/__init__.py b/vttcompilepy/__init__.py new file mode 100644 index 0000000..9d24135 --- /dev/null +++ b/vttcompilepy/__init__.py @@ -0,0 +1 @@ +from .vttcompilepy import * \ No newline at end of file diff --git a/vttcompilepy/__main__.py b/vttcompilepy/__main__.py new file mode 100644 index 0000000..c71be50 --- /dev/null +++ b/vttcompilepy/__main__.py @@ -0,0 +1,52 @@ +import vttcompilepy as vtt + +import argparse +import sys +from pathlib import Path + +def main(args=None): + if args is None: + args = sys.argv[1:] + parser = argparse.ArgumentParser() + + parser.add_argument("input", help="Input file") + parser.add_argument("output", help="Output file") + + cgroup = parser.add_argument_group("Compile options") + cgroup.add_argument("-a", "--all", action="store_true", help ="Compile everything") + + sgroup = parser.add_argument_group("Strip options") + sgroup.add_argument("-s", "--source", action="store_true", help="Strip source") + sgroup.add_argument("-b", "--hints", action="store_true", help="Strip source and hints") + sgroup.add_argument("-c", "--cache", action="store_true", help="Strip source and hints and cache tables") + + args = parser.parse_args(args) + + print(args.input) + print(args.output) + inpath = Path(args.input) + outpath = Path(args.output) + + compiler = vtt.Compiler(inpath) + + if args.all: + compiler.compile_all() + + #StripCommand strip = stripNothing; + #if (bStripCache) strip = stripBinary; + #else if (bStripHints) strip = stripHints; + #else if (bStripSource) strip = stripSource; + + level = vtt.StripLevel.STRIP_NOTHING + + if args.cache: + level = vtt.StripLevel.STRIP_BINARY + elif args.hints: + level = vtt.StripLevel.STRIP_HINTS + elif args.source: + level = vtt.StripLevel.STRIP_SOURCE + + compiler.save_font(outpath, level) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/vttcompilepy/cvttcompilepy.pxd b/vttcompilepy/cvttcompilepy.pxd new file mode 100644 index 0000000..e71afed --- /dev/null +++ b/vttcompilepy/cvttcompilepy.pxd @@ -0,0 +1,31 @@ +#cython: language_level=3 +#distutils: language = c++ +from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t +from libcpp cimport bool +from libc.stddef cimport wchar_t +from libcpp.string cimport string + +cdef extern from "..\\src\\pch.h": + + ctypedef enum StripCommand: + stripNothing + stripSource + stripHints + stripBinary + stripEverything + +cdef extern from "..\\src\\application.h": + + cdef cppclass Application: + Application() except + + bool Create() + bool OpenFont(string fileName, wchar_t* errMsg) + bool SaveFont(StripCommand strip, wchar_t* errMsg) + bool SaveFont(string fileName, StripCommand strip, wchar_t* errMsg) + + bool GotoFont(wchar_t* errMsg) + + bool CompileGlyphRange(uint16_t g1, uint16_t g2, bool quiet, wchar_t* errMsg) + bool CompileAll(bool quiet, wchar_t* errMsg) + + char* wCharToChar(char* out1, const wchar_t* in1) diff --git a/vttcompilepy/vttcompilepy.pyx b/vttcompilepy/vttcompilepy.pyx new file mode 100644 index 0000000..d948771 --- /dev/null +++ b/vttcompilepy/vttcompilepy.pyx @@ -0,0 +1,77 @@ +#cython: language_level=3 +#distutils: language = c++ +from enum import IntEnum +from .cvttcompilepy cimport * +from pathlib import Path + +class CompileError(Exception): + pass + +class StripLevel(IntEnum): + STRIP_NOTHING = stripNothing + STRIP_SOURCE = stripSource + STRIP_HINTS = stripHints + STRIP_BINARY = stripBinary + STRIP_EVERYTHING = stripEverything + +cdef class Compiler: + cdef Application* app_ # Hold C++ instance + + def __cinit__(self): + self.app_ = new Application() + + def __cinit__(self, path: Path): + self.app_ = new Application() + self.app_.Create() + cdef string src = bytes(path) + cdef wchar_t werr[1024] + cdef char err[1024] + result = self.app_.OpenFont(src, werr) + if result != True: + raise FileNotFoundError(self.app_.wCharToChar(err, werr)) + result = self.app_.GotoFont(werr) + if result != True: + raise FileNotFoundError(self.app_.wCharToChar(err, werr)) + + def __dealloc__(self): + del self.app_ + + def compile_all(self) -> None: + cdef wchar_t werr[1024] + cdef char err[1024] + result = self.app_.CompileAll(True, werr) + if(result != True): + raise CompileError(self.app_.wCharToChar(err, werr)) + + def compile_glyph_range(self, start: int = 0, end: int = 0) -> None: + cdef wchar_t werr[1024] + cdef char err[1024] + result = self.app_.CompileGlyphRange(start, end, True, werr) + if(result != True): + raise CompileError(self.app_.wCharToChar(err, werr)) + + def save_font(self, path: Path, level: StripLevel) -> None: + cdef string dest = bytes(path) + cdef wchar_t werr[1024] + cdef char err[1024] + result = self.app_.SaveFont(dest, level, werr) + if(result != True): + raise FileNotFoundError(self.app_.wCharToChar(err, werr)) + + def save_font(self, level: StripLevel) -> None: + cdef wchar_t werr[1024] + cdef char err[1024] + result = self.app_.SaveFont(level, werr) + if(result != True): + raise FileNotFoundError(self.app_.wCharToChar(err, werr)) + + + + + + + + + + + From 04d97cb337da697fa523c5bb6343b37880ee942d Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Mon, 15 Nov 2021 15:46:38 -0800 Subject: [PATCH 05/25] Update XCode project with application.cpp/h --- vttcompile/vttcompile.xcodeproj/project.pbxproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vttcompile/vttcompile.xcodeproj/project.pbxproj b/vttcompile/vttcompile.xcodeproj/project.pbxproj index 65de356..8cfc135 100644 --- a/vttcompile/vttcompile.xcodeproj/project.pbxproj +++ b/vttcompile/vttcompile.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 567685F62707D39F00391506 /* TTEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 567685E32707D39F00391506 /* TTEngine.cpp */; }; 567685F72707D39F00391506 /* vttcompile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 567685E42707D39F00391506 /* vttcompile.cpp */; }; 567685F82707D39F00391506 /* Variation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 567685E62707D39F00391506 /* Variation.cpp */; }; + 7A45EDB3274323060005BB8F /* application.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A45EDB1274321BD0005BB8F /* application.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -76,6 +77,8 @@ 567685E52707D39F00391506 /* Platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Platform.h; sourceTree = ""; }; 567685E62707D39F00391506 /* Variation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Variation.cpp; sourceTree = ""; }; 567685E72707D39F00391506 /* opentypedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opentypedefs.h; sourceTree = ""; }; + 7A45EDB1274321BD0005BB8F /* application.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = application.cpp; sourceTree = ""; }; + 7A45EDB2274321BD0005BB8F /* application.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = application.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -108,6 +111,8 @@ 567685B92707D35100391506 /* vttcompile */ = { isa = PBXGroup; children = ( + 7A45EDB1274321BD0005BB8F /* application.cpp */, + 7A45EDB2274321BD0005BB8F /* application.h */, 567685DE2707D39F00391506 /* CvtManager.cpp */, 567685E02707D39F00391506 /* CvtManager.h */, 567685D72707D39E00391506 /* File.cpp */, @@ -220,6 +225,7 @@ 567685EE2707D39F00391506 /* TextBuffer.cpp in Sources */, 567685EA2707D39F00391506 /* TTAssembler.cpp in Sources */, 567685F22707D39F00391506 /* Memory.cpp in Sources */, + 7A45EDB3274323060005BB8F /* application.cpp in Sources */, 567685F42707D39F00391506 /* TMTParser.cpp in Sources */, 567685E82707D39F00391506 /* Platform.cpp in Sources */, 567685F32707D39F00391506 /* CvtManager.cpp in Sources */, From 181e0b6966ec1383cc5247570dec53a91bd2fc01 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 16 Nov 2021 15:18:38 -0800 Subject: [PATCH 06/25] Update Cython files to for Mac compile. --- setup.py | 7 +++---- vttcompilepy/cvttcompilepy.pxd | 6 ++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 975bc07..4ceff4e 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,11 @@ linetrace = True define_macros.append(('CYTHON_TRACE_NOGIL', '1')) -extra_compile_args = ['/Zc:wchar_t'] +extra_compile_args = [''] if platform.system() != 'Windows': - extra_compile_args.append('-std=c++11') - + extra_compile_args.append('-std=c++1y') + extension = Extension( 'vttcompilepy.vttcompilepy', define_macros=define_macros, @@ -45,7 +45,6 @@ classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", - "Operating System :: Microsoft :: Windows :: Windows 10", ], package_dir={"": "."}, packages=["vttcompilepy"], diff --git a/vttcompilepy/cvttcompilepy.pxd b/vttcompilepy/cvttcompilepy.pxd index e71afed..05d2d18 100644 --- a/vttcompilepy/cvttcompilepy.pxd +++ b/vttcompilepy/cvttcompilepy.pxd @@ -5,7 +5,8 @@ from libcpp cimport bool from libc.stddef cimport wchar_t from libcpp.string cimport string -cdef extern from "..\\src\\pch.h": +#cdef extern from "..\\src\\pch.h": +cdef extern from "pch.h": ctypedef enum StripCommand: stripNothing @@ -14,7 +15,8 @@ cdef extern from "..\\src\\pch.h": stripBinary stripEverything -cdef extern from "..\\src\\application.h": +#cdef extern from "..\\src\\application.h": +cdef extern from "application.h": cdef cppclass Application: Application() except + From 09b4ee528d4ee14a5d69c1047a7b6365cc36fa37 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 16 Nov 2021 16:07:48 -0800 Subject: [PATCH 07/25] Remove temp dev comment --- vttcompilepy/cvttcompilepy.pxd | 2 -- 1 file changed, 2 deletions(-) diff --git a/vttcompilepy/cvttcompilepy.pxd b/vttcompilepy/cvttcompilepy.pxd index 05d2d18..fe829b2 100644 --- a/vttcompilepy/cvttcompilepy.pxd +++ b/vttcompilepy/cvttcompilepy.pxd @@ -5,7 +5,6 @@ from libcpp cimport bool from libc.stddef cimport wchar_t from libcpp.string cimport string -#cdef extern from "..\\src\\pch.h": cdef extern from "pch.h": ctypedef enum StripCommand: @@ -15,7 +14,6 @@ cdef extern from "pch.h": stripBinary stripEverything -#cdef extern from "..\\src\\application.h": cdef extern from "application.h": cdef cppclass Application: From bd52ae37c29e460c7951020bd6014fe4c4e480e3 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 17 Nov 2021 16:03:15 -0800 Subject: [PATCH 08/25] Resolve some clang warnings. --- src/CvtManager.cpp | 6 +++--- src/File.cpp | 4 ++-- src/TMTParser.cpp | 33 +++++++++++++++++---------------- src/TTAssembler.cpp | 24 ++++++++++++------------ src/TTEngine.cpp | 4 ++-- src/TTFont.cpp | 18 +++++++++--------- src/TTFont.h | 2 +- src/TTGenerator.cpp | 16 ++++++++-------- src/Variation.cpp | 2 +- src/application.h | 1 - src/vttcompile.cpp | 4 ++-- 11 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/CvtManager.cpp b/src/CvtManager.cpp index b0c6849..1bf4f15 100644 --- a/src/CvtManager.cpp +++ b/src/CvtManager.cpp @@ -869,7 +869,7 @@ bool PrivateControlValueTable::SettingsDeclaration(void) { bool PrivateControlValueTable::CvtDeclaration(uint32_t *attribute) { int32_t cvtNum; - ControlValue *cvt; + ControlValue *cvt = nullptr; if (!(this->AttributeAssociation(attribute) && // this->InlineSttmt() && this->ValueAssociation(*attribute,&cvtNum,&cvt) && this->InlineSttmt() && @@ -958,7 +958,7 @@ bool PrivateControlValueTable::InheritanceRelation(int32_t cvtNum, ControlValue if (!this->Parameter(&parentCvtNumParam)) return false; if (!AssertNatural(&parentCvtNumParam,0,maxCvtNum-1,L"Parent cvt number",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } parentCvt = &this->tempData[parentCvtNumParam.value]; - if (!parentCvt->flags & cvtDefined) { swprintf(this->errMsg,L"Parent cvt not defined (must be completely defined prior to tying child cvts to it)"); this->scanner.ErrUnGetSym(); return false; } + if (!(parentCvt->flags & cvtDefined)) { swprintf(this->errMsg,L"Parent cvt not defined (must be completely defined prior to tying child cvts to it)"); this->scanner.ErrUnGetSym(); return false; } // if (this->scanner.sym != at) { swprintf(this->errMsg,L"'@' expected"); return false; } // if (!this->scanner.GetSym()) return false; if (!this->legacyCompile) { @@ -1217,7 +1217,7 @@ bool PrivateControlValueTable::Compile(TextBuffer*source, TextBuffer*prepText, b int32_t i,firstAvailSubAttributeValue[numSubAttributes]; uint32_t currAttribute; Attribute *sortedAttributes; - bool memError; + bool memError = false; wchar_t comment[maxLineSize],dateTime[32]; this->legacyCompile = legacyCompile; diff --git a/src/File.cpp b/src/File.cpp index 6b2415d..f32567d 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -54,7 +54,7 @@ int32_t File::Length(void) { //return (m_hfile != INVALID_HANDLE_VALUE) ? GetFileSize(m_hfile, NULL) : 0; struct stat st; stat(m_fileName.c_str(), &st); - return st.st_size; + return static_cast(st.st_size); } // File::Length void File::SetPos(int32_t pos, bool truncate) { @@ -65,7 +65,7 @@ void File::SetPos(int32_t pos, bool truncate) { } // File::SetPos int32_t File::GetPos(void) { - return ftell((FILE*)m_hfile); + return static_cast(ftell((FILE*)m_hfile)); } // File::GetPos void File::ReadBytes(int32_t numBytes, void *buffer) { diff --git a/src/TMTParser.cpp b/src/TMTParser.cpp index 31f6b78..e1060f9 100644 --- a/src/TMTParser.cpp +++ b/src/TMTParser.cpp @@ -360,7 +360,7 @@ void AdjustFPs(short serifType, FormParam *formParams); void AdjustFPs(short serifType, FormParam *formParams) { /* SERIF < (type, knot0, knot1, ... knotn-1) param 0 1, 2, 3, param-1 */ - short params,i; + short params = 0,i; switch (serifType) { case 0: params = 9; break; @@ -916,7 +916,7 @@ void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar case xNoRound: case yNoRound: { short knot[maxParams]; - Rounding r; + Rounding r = rnone; for (i = 0; i < params; i++) knot[i] = (short)(param[i].numValue/one6); switch (cmd) { @@ -1012,7 +1012,8 @@ void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar bool y = cmd == resYIPAnchor, haveFlag = param[0].type == angleFlag, havePostRound = param[haveFlag].type == postRoundFlag; - ActParam *parent0Param = ¶m[haveFlag+havePostRound], *childParam = ¶m[haveFlag+havePostRound+1], *parent1Param = ¶m[haveFlag+havePostRound+2]; + ActParam *parent0Param = ¶m[haveFlag+havePostRound], *childParam = ¶m[haveFlag+havePostRound+1]; + // ActParam *parent1Param = ¶m[haveFlag+havePostRound+2]; short parent0 = (short)(parent0Param->numValue/one6),child = (short)(childParam->numValue/one6),parent1 = (short)(param[params-1].numValue/one6); ProjFreeVector projFreeVector; @@ -1091,8 +1092,8 @@ void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar dist = cmd == resXIPDDist || cmd == resYIPDDist, haveAngleFlag = false, // so far, TT fn not implemented orthogonally enough haveStrokeFlag = false; // so far, TT fn not implemented orthogonally enough - short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, - cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; + //short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, + // cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], *parent0Param = ¶m[haveAngleFlag+haveStrokeFlag+1], *child0Param = ¶m[haveAngleFlag+haveStrokeFlag+2], @@ -1129,8 +1130,8 @@ void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar dist = cmd == resXIPDDDist || cmd == resYIPDDDist, haveAngleFlag = param[0].type == angleFlag, haveStrokeFlag = false; // so far, TT fn not implemented orthogonally enough - short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, - cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; + // short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, + // cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], *parent0Param = ¶m[haveAngleFlag+haveStrokeFlag+1], *child0Param = ¶m[haveAngleFlag+haveStrokeFlag+2], @@ -1171,8 +1172,8 @@ void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar short cvtParamOffs0 = 2; bool haveCvt0 = !dist && params > cvtParamOffs0 && param[cvtParamOffs0].type == cvtN; short cvtParamOffs1 = 2 + haveCvt0 + 2; - bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN, - haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; + bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN; + // bool haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; ActParam *parent0Param = ¶m[0],*child0Param = ¶m[1],*parent1Param = ¶m[2+haveCvt0],*child1Param = ¶m[3+haveCvt0]; short parent0 = (short)(parent0Param->numValue/one6), child0 = (short)(child0Param->numValue/one6), @@ -1197,13 +1198,13 @@ void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar if (!this->legacyCompile) { bool dist = cmd == resIIPDDist; - short optParamOffs = cmd == resIIPDDist ? 6 : 8; + // short optParamOffs = cmd == resIIPDDist ? 6 : 8; short cvtParamOffs0 = 3; bool haveCvt0 = !dist && params > cvtParamOffs0 && param[cvtParamOffs0].type == cvtN; short cvtParamOffs1 = 3 + haveCvt0 + 2; - bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN, - haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; + bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN; + // bool haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; ActParam *grandParent0Param = ¶m[0],*parent0Param = ¶m[1],*child0Param = ¶m[2],*parent1Param = ¶m[3+haveCvt0],*child1Param = ¶m[4+haveCvt0],*grandParent1Param = ¶m[5+haveCvt0+haveCvt1]; short grandParent0 = (short)(grandParent0Param->numValue/one6), parent0 = (short)(parent0Param->numValue/one6), @@ -1242,7 +1243,7 @@ void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar void TMTSourceParser::XFormToNewSyntax(void) { /* this is a bit of a botched job, but I'd rather have the standard parameter checking mechanism do all the serious work */ - int32_t savePos,flagPos,parmPos; + int32_t savePos,flagPos,parmPos = 0; wchar_t old[32],neu[64]; short s,d,l; @@ -1445,7 +1446,7 @@ void TMTSourceParser::Parameter(ActParam *actParam) { ActParam colorParam; Symbol ttvSym; ParamType paramType; - int32_t numValue,firstLocalParamStart; + int32_t numValue,firstLocalParamStart = 0; bool gotKnot[2]; paramStart = this->prevPos; @@ -1973,7 +1974,7 @@ void TMTSourceParser::GetNumber(void) { Symbol Search(wchar_t *entry, short left, short right, short *matching); Symbol Search(wchar_t *entry, short left, short right, short *matching) { - short mid,diff,minMatch; + short mid,diff = 0,minMatch; wchar_t *id,*en; while (left <= right) { @@ -2276,4 +2277,4 @@ bool TMTRemoveAltCodePath(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyp In any case the X|YStroke had probably better be called X|YStem and expect 2 knots only, rather than 4, much like the X|YRound, or an even number of parameters? -*/ \ No newline at end of file +*/ diff --git a/src/TTAssembler.cpp b/src/TTAssembler.cpp index 4e62c66..61d77cc 100644 --- a/src/TTAssembler.cpp +++ b/src/TTAssembler.cpp @@ -1222,7 +1222,7 @@ wchar_t * TT_ReadInstructionBooleans (wchar_t * CurrentPtr, wchar_t * EOLPtr, sh booleanCount++ ) { booleanShift--; - for ( found = k = 0; k < NumberOfBooleanTranslations; k++ ) { + for ( found = k = 0; k < (short)NumberOfBooleanTranslations; k++ ) { if ( asm_booleanTranslation1[k].type == tt_instruction[InstructionIndex].booleans[booleanCount] && (asm_booleanTranslation1[k].code) == *CurrentPtr ) { @@ -2635,8 +2635,8 @@ wchar_t *TT_InnerCompile( } } else { /* this is a compiler switch */ - short i, found, SwitchCode; - for ( found = i = 0; i < TOTALNUMBEROFSWITCH; i++ ) { + short i, found, SwitchCode = 0; + for ( found = i = 0; i < (short)TOTALNUMBEROFSWITCH; i++ ) { if ( StringLength == (short) STRLENW( tt_CompilerSwitch[i].name ) && wcsncmp( CurrentPtr+1, tt_CompilerSwitch[i].name, StringLength) == 0 ) { @@ -2844,12 +2844,12 @@ wchar_t *TT_InnerCompile( } else { /* regular instruction */ short InstructionIndex, found; - unsigned short InstructionCode; /* unsigned short because of the fake code used to detect composite commands */ + unsigned short InstructionCode = 0; /* unsigned short because of the fake code used to detect composite commands */ int32_t StringLength; StringLength = TT_GetStringLength (CurrentPtr, EndPtr); - for ( found = InstructionIndex = 0; InstructionIndex < TOTALNUMBEROFINSTRUCTIONS; InstructionIndex++ ) { + for ( found = InstructionIndex = 0; InstructionIndex < (short)TOTALNUMBEROFINSTRUCTIONS; InstructionIndex++ ) { if ( StringLength == (short) STRLENW( tt_instruction[InstructionIndex].name ) && wcsncmp(CurrentPtr, tt_instruction[InstructionIndex].name, StringLength) == 0 ) { found = true; @@ -3574,7 +3574,7 @@ wchar_t * CO_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, { short argindex, argNb; wchar_t * tempP; - const wchar_t * argTypeBuffer = co_instruction[InstructionIndex].pops; + // const wchar_t * argTypeBuffer = co_instruction[InstructionIndex].pops; *argc = 0; argindex = 0; bool inRange; @@ -3681,7 +3681,7 @@ wchar_t * CO_ReadInstructionBooleans (wchar_t * CurrentPtr, wchar_t * EOLPtr, sh booleanCount++ ) { booleanShift--; - for ( found = k = 0; k < co_NumberOfBooleanTranslations; k++ ) { + for ( found = k = 0; k < (short)co_NumberOfBooleanTranslations; k++ ) { if ( co_booleanTranslation[k].type == co_instruction[InstructionIndex].booleans[booleanCount] && (co_booleanTranslation[k].code) == *CurrentPtr ) { @@ -3734,7 +3734,7 @@ wchar_t *CO_Compile(TrueTypeFont * font, TrueTypeGlyph * glyph, wchar_t *StartPt // in a window that is being slided along the code. This appears to be used to determine, whether such instructions // as USEMYMETRICS or OVERLAP and NONOVERLAP (the latter two being obsolete, as far as I understand the TT manual), // are properly followed by further composite instructions... - short LineNb,LastLineCompiled,RoundingCode,currInstrIndex,prevInstrIndex,args[256],argc; + short LineNb,LastLineCompiled,RoundingCode = 0,currInstrIndex = 0,prevInstrIndex = 0,args[256],argc = 0; bool currInstrIsCompInstr,prevInstrIsCompInstr; int32_t LineLength,StringLength; wchar_t *CurrentPtr,*EOLPtr; @@ -3781,8 +3781,8 @@ wchar_t *CO_Compile(TrueTypeFont * font, TrueTypeGlyph * glyph, wchar_t *StartPt // regular instruction StringLength = TT_GetStringLength (CurrentPtr, EndPtr); currInstrIndex = 0; - while (currInstrIndex < CONUMBEROFINSTRUCTIONS && !(StringLength == (int32_t)STRLENW(co_instruction[currInstrIndex].name) && wcsncmp(CurrentPtr, co_instruction[currInstrIndex].name, StringLength) == 0)) currInstrIndex++; - currInstrIsCompInstr = currInstrIndex < CONUMBEROFINSTRUCTIONS; + while (currInstrIndex < (short)CONUMBEROFINSTRUCTIONS && !(StringLength == (int32_t)STRLENW(co_instruction[currInstrIndex].name) && wcsncmp(CurrentPtr, co_instruction[currInstrIndex].name, StringLength) == 0)) currInstrIndex++; + currInstrIsCompInstr = currInstrIndex < (short)CONUMBEROFINSTRUCTIONS; if (prevInstrIsCompInstr) { font->UpdateCompositeProfile(glyph,&compositeProfile,currInstrIsCompInstr ? CO_CompInstrFollow : CO_StdInstrFollow, RoundingCode, prevInstrIndex, args, argc, &Newbbox, co_error); @@ -3924,7 +3924,7 @@ void CO_GetErrorString (short ErrorNb, wchar_t * ErrorString) } bool DisassemComponent(TrueTypeGlyph *glyph, TextBuffer *src, wchar_t errMsg[]) { - short i,flags,glyphIndex,arg1,arg2,xscale,yscale,scale01,scale10; + short i,flags,glyphIndex,arg1,arg2,xscale = 0,yscale = 0,scale01 = 0,scale10 = 0; wchar_t c,buf[maxLineSize]; i = 0; @@ -4108,4 +4108,4 @@ bool TTAssemble(ASMType asmType, TextBuffer* src, TrueTypeFont* font, TrueTypeGl return CompileError == tt_NoError; -} \ No newline at end of file +} diff --git a/src/TTEngine.cpp b/src/TTEngine.cpp index fc00879..a9e79a9 100644 --- a/src/TTEngine.cpp +++ b/src/TTEngine.cpp @@ -1081,7 +1081,7 @@ void TTSourceEngine::ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short paren this->usedpv = false; this->lastChild = invalidKnotNum; this->rp[0] = this->rp[2] = child; - parent1; + // parent1; } // TTSourceEngine::ResIPMDAP void TTSourceEngine::ResMIRP(short parent, short child, short cvt, bool useMinDist) { @@ -1515,4 +1515,4 @@ void GenPrepEndIf(TextBuffer *prep) { TTEngine *NewTTSourceEngine(void) { return new TTSourceEngine; -} \ No newline at end of file +} diff --git a/src/TTFont.cpp b/src/TTFont.cpp index 2161eb6..eb9b018 100644 --- a/src/TTFont.cpp +++ b/src/TTFont.cpp @@ -454,12 +454,12 @@ bool TrueTypeFont::SetSfnt(short platformID, short encodingID, wchar_t errMsg[]) axisRecord.minValue.SetRawValue(minValue); packed1 += 2; - int32_t* pdefaultValue = reinterpret_cast(packed1); + // int32_t* pdefaultValue = reinterpret_cast(packed1); int32_t defaultValue = SWAPL(*pminValue); axisRecord.defaultValue.SetRawValue(defaultValue); packed1 += 2; - int32_t* pmaxValue = reinterpret_cast(packed1); + // int32_t* pmaxValue = reinterpret_cast(packed1); int32_t maxValue = SWAPL(*pminValue); axisRecord.maxValue.SetRawValue(maxValue); packed1 += 2; @@ -1534,7 +1534,7 @@ bool TrueTypeFont::MergePrivateCvarWithInstanceManager(const TSICHeader &header return true; auto instanceManager = this->GetInstanceManager(); - auto axisCount = this->GetVariationAxisCount(); + // auto axisCount = this->GetVariationAxisCount(); std::vector recordMerged; recordMerged.resize(header.recordCount, false); @@ -1690,7 +1690,7 @@ int32_t TrueTypeFont::UpdateCvar(int32_t *size, unsigned char data[]) std::stable_sort(cvarTuples.begin(), cvarTuples.end(), Compare_Tuples_by_Order); auto axisCount = this->GetVariationAxisCount(); - auto tupleCount = cvarTuples.size(); + // auto tupleCount = cvarTuples.size(); size_t numTuplesWithData = 0; size_t numIntermediates = 0; // Among numTuplesWithData how many intermediates @@ -2573,8 +2573,8 @@ bool TrueTypeFont::BuildNewSfnt(StripCommand strip, CharGroup group, int32_t gly wchar_t errMsg[]) { unsigned char *sfnt,*tmpSfnt,*tmpSfntHandle; - int32_t headerSize,i,j,tag,numberOfGlyphs,numberOfHMetrics,zero = 0L,pad; - uint32_t sfntPos,tmpSfntSize,sizeOfTable,newSfntSizeEstimate; + int32_t headerSize,i,j,tag,numberOfGlyphs,numberOfHMetrics = 0,zero = 0L,pad; + uint32_t sfntPos = 0,tmpSfntSize,sizeOfTable,newSfntSizeEstimate; sfnt_FontHeader *head; sfnt_HorizontalHeader *hhea; bool result = false; @@ -3216,8 +3216,8 @@ bool TrueTypeFont::IncrBuildNewSfnt( wchar_t errMsg[]) { unsigned char *sfnt, *tmpSfnt, *tmpSfntHandle; - int32_t headerSize, i, j, j2, tag, numberOfGlyphs, numberOfHMetrics, zero = 0L, pad; - uint32_t sfntPos, tmpSfntSize, sizeOfTable, newSfntSizeEstimate; + int32_t headerSize, i, j, j2, tag, numberOfGlyphs = 0, numberOfHMetrics = 0, zero = 0L, pad; + uint32_t sfntPos = 0, tmpSfntSize, sizeOfTable, newSfntSizeEstimate; sfnt_FontHeader *head; bool result = false; @@ -4587,7 +4587,7 @@ uint32_t TrueTypeFont::PackGlyphs(StripCommand strip, TrueTypeGlyph *glyph, int3 uint32_t TrueTypeFont::PackGlyph(unsigned char *dst, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfInstruction, sfnt_HorizontalMetrics *hmtx) { unsigned char *pStart; uint32_t size; - short i, numberOfPoints, x, y, delta, j, whoseMetrics; + short i, numberOfPoints, x, y, delta, j, whoseMetrics = 0; unsigned char bitFlags; bool composite,useMyMetrics; diff --git a/src/TTFont.h b/src/TTFont.h index 1f8339e..5427784 100644 --- a/src/TTFont.h +++ b/src/TTFont.h @@ -490,7 +490,7 @@ class TrueTypeFont { FontMetricProfile metricProfile; FontMetricProfile newMetricProfile; // used for 'maxp' computation - bool useIntegerScaling; + //bool useIntegerScaling; unsigned short macStyle; // passed from ReadHeader to WriteHeader as is diff --git a/src/TTGenerator.cpp b/src/TTGenerator.cpp index d0cc2b6..7968452 100644 --- a/src/TTGenerator.cpp +++ b/src/TTGenerator.cpp @@ -481,9 +481,9 @@ void TTSourceGenerator::Link(bool y, bool dist, ProjFreeVector *projFreeVector, Vector link; RVector linkDirection; double vectProd,scalProd; - short distance,parentC,childC; + short distance = 0,parentC,childC; LinkColor color; - Rounding deltaR,parentR,childR; + Rounding deltaR = rthg,parentR,childR; bool italicLink,lsbLink,rsbLink,negativeDist,negativeMirp; linkDirection.x = 0; @@ -1826,7 +1826,7 @@ void TTSourceGenerator::Serif(bool forward, short type, short knots, short knot[ void TTSourceGenerator::Scoop(short parent0, short child, short parent1, wchar_t error[]) { Vector base; - bool ok,y; + bool ok,y = false; short dist,actualCvt; base = SubV(this->V[parent1],this->V[parent0]); @@ -2217,8 +2217,8 @@ FVMTDirection TTSourceGenerator::CalcDiagonalFVMT(FVOverride fv, short parent0, AlignParam *alignParam = nullptr; bool previousStroke,previousAlign; short neighbour = this->Neighbour(parent0,parent1,child,true); - short deltaAngle = this->attrib[child].deltaAngle; - short diagChild,alignChild,otherChild; + // short deltaAngle = this->attrib[child].deltaAngle; + short diagChild = 0,alignChild,otherChild; Vector D = SubV(this->V[neighbour],this->V[child]); FVMTDirection fvmt; @@ -2286,7 +2286,7 @@ FVMTDirection TTSourceGenerator::CalcAlignFVMT(FVOverride fv, short parent0, sho bool previousStroke,previousAlign; short neighbour = this->Neighbour(parent0,parent1,child,true); short deltaAngle = attrib->deltaAngle; - short diagChild,alignChild,otherChild; + short diagChild = 0,alignChild,otherChild; Vector D = SubV(this->V[neighbour],this->V[child]); FVMTDirection fvmt; @@ -2391,7 +2391,7 @@ short TTSourceGenerator::TheCvt(short parent, short child, LinkColor color, Link which function is to be called depending on what VacuFormRound type and configuration of knots... For the curious: cf. fdefs0.c *****/ void TTSourceGenerator::DoVacuFormRound(void) { - short pass,labelNumber,labels,i,j,first,last,lo,hi,l,h,type,x1,x2,y1,y2,nextX1,nextX2,nextY1,nextY2,base1,n1,inc1,dec1,base2,n2,inc2,dec2,fun,knots,knot[maxVacuForms],next,dist; + short pass,labelNumber,labels = 0,i,j,first,last,lo,hi,l,h,type,x1,x2,y1,y2,nextX1,nextX2,nextY1,nextY2,base1,n1,inc1,dec1,base2,n2,inc2,dec2,fun,knots,knot[maxVacuForms],next,dist; VacuFormParams *vacu; wchar_t buf[32]; @@ -2598,4 +2598,4 @@ TTGenerator *NewTTSourceGenerator(void) { checks that apply to all code generators, and preferreably are done once and forever (ie. in the parser), such as not to try to VacuFormRound the side bearing points, etc. - */ \ No newline at end of file + */ diff --git a/src/Variation.cpp b/src/Variation.cpp index 37587e2..557842f 100644 --- a/src/Variation.cpp +++ b/src/Variation.cpp @@ -117,7 +117,7 @@ bool CVTVariationInterpolator1::ReverseInterpolate(const std::vector &d tuples[tupleIndex]->intermediateStartF2Dot14.clear(); tuples[tupleIndex]->intermediateEndF2Dot14.clear(); - std::vector &peak = sortedAxisLocations[i].peakFloat; + // std::vector &peak = sortedAxisLocations[i].peakFloat; // Determine the intermediate start/end if needed. bool intermediateNeeded = false; diff --git a/src/application.h b/src/application.h index cea93a7..01b88dd 100644 --- a/src/application.h +++ b/src/application.h @@ -33,7 +33,6 @@ class Application wchar_t mainFont[maxLineSize], textFont[maxLineSize]; - bool fontOpen = false; std::unique_ptr font = nullptr; std::unique_ptr glyph = nullptr; std::string fileName; diff --git a/src/vttcompile.cpp b/src/vttcompile.cpp index 0db42c6..f2db24f 100644 --- a/src/vttcompile.cpp +++ b/src/vttcompile.cpp @@ -118,7 +118,7 @@ int main(int argc, char* argv[]) { try { - g1 = g2 = std::stoul(sg1); + g1 = g2 = static_cast(std::stoul(sg1)); } catch (...) { @@ -132,7 +132,7 @@ int main(int argc, char* argv[]) { try { - g2 = std::stoul(sg2); + g2 = static_cast(std::stoul(sg2)); } catch (...) { From e8a97c989ad9a6996a144453d0896c9e6800b8f0 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 17 Nov 2021 17:15:54 -0800 Subject: [PATCH 09/25] Create py_ext_main.yml --- .github/workflows/py_ext_main.yml | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/py_ext_main.yml diff --git a/.github/workflows/py_ext_main.yml b/.github/workflows/py_ext_main.yml new file mode 100644 index 0000000..09cbb74 --- /dev/null +++ b/.github/workflows/py_ext_main.yml @@ -0,0 +1,40 @@ +name: Python Extension + +on: + # Triggers the workflow on push or pull request events but only for the main branch + push: + branches: [ main ] + pull_request: + branches: [ main ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + # The type of runner that the job will run on + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + strategy: + fail-fast: false + matrix: + os: [ubunto-latest, macos-latest, windows-latest] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.x" + - name: Install dependencies + run: python -m cibuildwheel --output-dir wheelhouse + - uses: actions/upload-artifact@v2 + with: + name: vttcompilepy-${{ matrix.os }} + path: wheelhouse/*.whl From cb2531308252aa364c06eb66662a9725098b8152 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 17 Nov 2021 17:35:34 -0800 Subject: [PATCH 10/25] Update py_ext_main.yml --- .github/workflows/py_ext_main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/py_ext_main.yml b/.github/workflows/py_ext_main.yml index 09cbb74..a0fb20a 100644 --- a/.github/workflows/py_ext_main.yml +++ b/.github/workflows/py_ext_main.yml @@ -33,6 +33,8 @@ jobs: with: python-version: "3.x" - name: Install dependencies + run: pip install cibuildwheel + - name: Build Wheels run: python -m cibuildwheel --output-dir wheelhouse - uses: actions/upload-artifact@v2 with: From 4e6a7d9b41a6751b7824d58700f800c7c0863a74 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 17 Nov 2021 17:48:38 -0800 Subject: [PATCH 11/25] Update py_ext_main.yml --- .github/workflows/py_ext_main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py_ext_main.yml b/.github/workflows/py_ext_main.yml index a0fb20a..7defacc 100644 --- a/.github/workflows/py_ext_main.yml +++ b/.github/workflows/py_ext_main.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubunto-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] # Steps represent a sequence of tasks that will be executed as part of the job steps: From d3cd28110ea787e8b2b9ba5183e65f47bf7a4cb5 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Mon, 6 Dec 2021 17:26:31 -0800 Subject: [PATCH 12/25] Use comforming swprintf() calls. --- src/CvtManager.cpp | 3513 +++++++-------- src/CvtManager.h | 154 +- src/File.cpp | 350 +- src/File.h | 58 +- src/Memory.cpp | 39 +- src/TMTParser.cpp | 4600 +++++++++---------- src/TMTParser.h | 50 +- src/TTAssembler.cpp | 8221 +++++++++++++++++----------------- src/TTAssembler.h | 46 +- src/TTEngine.cpp | 3097 ++++++------- src/TTEngine.h | 276 +- src/TTFont.cpp | 10036 +++++++++++++++++++++--------------------- src/TTFont.h | 1128 ++--- src/TTGenerator.cpp | 5201 +++++++++++----------- src/TTGenerator.h | 144 +- src/TextBuffer.cpp | 719 ++- src/application.cpp | 1041 +++-- src/application.h | 82 +- src/pch.h | 131 +- src/vttcompile.cpp | 435 +- 20 files changed, 19739 insertions(+), 19582 deletions(-) diff --git a/src/CvtManager.cpp b/src/CvtManager.cpp index 1bf4f15..b225c37 100644 --- a/src/CvtManager.cpp +++ b/src/CvtManager.cpp @@ -1,1750 +1,1763 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include // swprintf -#include // wcscpy, memcpy -#include "pch.h" -#include "List.h" -#include "CvtManager.h" -#include "TTEngine.h" // maxPpemSize & al. - -// #define DEBUGCVT - -/***** from cluster.h, for backwards compatibility with existing "high-level" control value tables (cvt "comments") - - 1 1 1 1 1 1 - 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 -+---+-----+---+-----+-----------+ -| ? |Group| C | Dir | Feature | -+---+-----+---+-----+-----------+ - -14 bits used => use 14th bit to indicate a defined cvt entry - -*****/ - -/* group 3 bits */ -#define C_ANYGROUP 0 -#define C_UC 1 -#define C_LC 2 -#define C_FIG 3 -#define C_OTHER 4 -//#define C_RESERVED1 5 -#define C_NONLATIN 5 // @@TM::LIGHT -#define C_RESERVED2 6 -#define C_RESERVED3 7 -#define groupPos (colorPos + colorBits) -#define groupBits 3 -#define groupMask (~((uint32_t)(-1) << groupBits)) - -/* color 2 bits */ -#define C_GREY 0 -#define C_BLACK 1 -#define C_WHITE 2 -#define C_ANYCOLOR 3 -#define colorPos (dirPos + dirBits) -#define colorBits 2 -#define colorMask (~((uint32_t)(-1) << colorBits)) - -/* direction 3 bits */ -#define C_XDIR 1 -#define C_YDIR 2 -#define C_DIAG 3 -#define C_ANYDIR 4 -#define dirPos (catPos + catBits) -#define dirBits 3 -#define dirMask (~((uint32_t)(-1) << dirBits)) - -/* features 6 bits */ -#define C_DUMMY 0 -#define C_STROKE 1 -#define C_ROUND 2 -#define C_DISTANCE 3 -#define C_BEND_RADIUS 4 -#define C_LSB 5 -#define C_RSB 6 -#define C_SERIF_THIN 7 -#define C_SERIF_HEIGHT 8 -#define C_SERIF_EXT 9 -#define C_SERIF_CRVHGT 10 /* for triangular serifs */ -#define C_SERIF_OTHER 11 /* for triangular serifs */ -#define C_BLACKBODYWIDTH 12 -#define C_SERIFBOTTOM 13 -#define C_BRANCH_BEND_RADIUS 14 /* Type 2 bends */ -#define C_SCOOP_DEPTH 15 -#define C_NEW_STRAIGHT 16 -#define C_NEW_DIAGONAL 17 -#define C_NEW_ANYDIR 18 -#define C_SQUARE_HEIGHT 19 // new cvt category for absolute (square) heights -#define C_ROUND_HEIGHT 20 // new cvt category for relative (round, accent) heights -#define C_ITALIC_RUN 21 // new cvt category, too -#define C_ITALIC_RISE 22 // new cvt category, too -#define catPos 0 -#define catBits 6 -#define catMask (~((uint32_t)(-1) << catBits)) - -// @@TM::LIGHT - begin -const short charGroupToInt[numCharGroups] = {C_ANYGROUP, C_OTHER, C_UC, C_LC, C_FIG, C_NONLATIN, C_RESERVED2, C_RESERVED3}; -const wchar_t charGroupToStrg[numCharGroups][cvtAttributeStrgLen] = {L"AnyGroup", L"Other", L"UpperCase", L"LowerCase", L"Figure", L"Reserved1", L"Reserved2", L"Reserved3"}; -const wchar_t charGroupToSpacingText[numCharGroups][cvtAttributeStrgLen] = {L" ", L"HH HOHO OO ", L"HH HOHO OO ", L"nn nono oo ", L"11 1010 00 ", L" ", L" ", L" "}; -const CharGroup intToCharGroup[numCharGroups] = {anyGroup, upperCase, lowerCase, figureCase, otherCase, nonLatinCase, reservedCase2, reservedCase3}; -// @@TM::LIGHT - end - - -const short linkColorToInt[numLinkColors] = {C_ANYCOLOR, C_BLACK, C_GREY, C_WHITE}; -const wchar_t linkColorToStrg[numLinkColors][cvtAttributeStrgLen] = {L"AnyColor", L"Black", L"Grey", L"White"}; -const LinkColor intToLinkColor[numLinkColors] = {linkGrey, linkBlack, linkWhite, linkAnyColor}; - -const short linkDirectionToInt[numLinkDirections] = {C_ANYDIR, C_XDIR, C_YDIR, C_DIAG}; -const wchar_t linkDirectionToStrg[numLinkDirections][cvtAttributeStrgLen] = {L"AnyDirection", L"X", L"Y", L"Diag"}; -const LinkDirection intToLinkDirection[1 + numLinkDirections] = {linkAnyDir, linkX, linkY, linkDiag, linkAnyDir}; - -const short cvtCategoryToInt[numCvtCategories] = {C_DUMMY, C_DISTANCE, C_STROKE, C_ROUND, - C_LSB, C_RSB, C_BLACKBODYWIDTH, C_SERIF_THIN, C_SERIF_HEIGHT, C_SERIF_EXT, C_SERIF_CRVHGT, C_SERIF_OTHER, C_SERIFBOTTOM, - C_SQUARE_HEIGHT,C_ROUND_HEIGHT,C_ITALIC_RUN,C_ITALIC_RISE, - C_BEND_RADIUS, C_BRANCH_BEND_RADIUS, C_SCOOP_DEPTH, C_NEW_STRAIGHT, C_NEW_DIAGONAL, C_NEW_ANYDIR}; -const wchar_t cvtCategoryToStrg[numCvtCategories][cvtAttributeStrgLen] = {L"AnyCategory", L"Distance", L"StraightStroke", L"RoundStroke", - L"LSB", L"RSB", L"BlackBodyWidth", L"SerifThin", L"SerifHeight", L"SerifExt", L"SerifCurveHeight", L"SerifOther", L"SerifBottom", - L"SquareHeight", L"RoundHeight", L"ItalicRun", L"ItalicRise", - L"BendRadius", L"BranchBendRadius", L"ScoopDepth", L"NewStraight", L"NewDiagonal", L"NewAnyFeature"}; -const CvtCategory intToCvtCategory[numCvtCategories] = {cvtAnyCategory, cvtStroke, cvtRound, cvtDistance, - cvtBendRadius, cvtLsb, cvtRsb, cvtSerifThin, cvtSerifHeight, cvtSerifExt, cvtSerifCurveHeight, cvtSerifOther, cvtBlackBody, - cvtSerifBottom, cvtBranchBendRadius, cvtScoopDepth, cvtNewStraightStroke, cvtNewDiagStroke, cvtNewAnyCategory, cvtSquareHeight, cvtRoundHeight, cvtItalicRun, cvtItalicRise}; - -#define DebugS(s) (ParentView::ActiveApplication()->ApplicationError(s)) - -#define lookahead 2 // #chars - -typedef enum {group = 0, color, direction, category, // attribute declaration and settings first (color and direction are only dummies), - fpgmBias, - instructionsOn, dropOutCtrlOff, scanCtrl, scanType, cvtCutIn, - clearTypeCtrl, linearAdvanceWidths, // they are reserved identifiers, - cvtDelta, bCvtDelta, gCvtDelta, // so are these - asM, ident, // user defined identifier next, acts as sentinel in case searched identifier is not reserved - natural, hexadecimal, rational, literal, - period, comma, ellipsis, colon, semiColon, percent, at, - leftParen, plus, minus, times, divide, rightParen, equals, relatesTo, eot} Symbol; // followed by all the other symbols (ordered, don't re-order) - -#define numKeyWords (int32_t)ident -#define maxKeyWordLen 32 -const wchar_t keyWord[numKeyWords][maxKeyWordLen] = {L"GROUP", L"COLOR", L"DIRECTION", L"CATEGORY", - L"FPgmBias", - L"InstructionsOn", L"DropOutCtrlOff", L"ScanCtrl", L"ScanType", L"CvtCutIn", - L"ClearTypeCtrl", L"LinearAdvanceWidths", - L"Delta", L"BDelta", L"GDelta", L"ASM" }; // cf. above - -#define firstSetting fpgmBias -#define lastSetting linearAdvanceWidths -#define numSettings (lastSetting - firstSetting + 1) - -typedef struct Scanner { // make it a static class, neither want to dynamically allocate nor subclass it -public: - Symbol sym; - int32_t value; - wchar_t literal[maxAsmSize]; - bool Init(TextBuffer*source, File *file, wchar_t errMsg[]); - void Term(int32_t *errPos, int32_t *errLen); - bool GetSym(void); - void ReplaceIdent(const wchar_t capIdent[]); - void ErrUnGetSym(void); -private: - void GetCh(void); - bool SkipComment(void); - bool Skip(void); - bool GetNum(void); - bool GetIdent(void); - bool GetLiteral(void); - TextBuffer*source; // for replacing properly capitalized text - File *file; // when compiling character group - int32_t pos,len; // into and of text - wchar_t *text; - wchar_t ch,ch2; // 2-char look-ahead - int32_t prevSymPos,prevSymEnd,symPos; // symPos >= 0 => error - wchar_t *errMsg; -} Scanner; - -#define numSubAttributes (int32_t)instructionsOn -#define maxSubAttributes 0x100 // e.g. max # character groups -#define subAttributeBits 8 -#define subAttributeMask (~((uint32_t)(-1) << subAttributeBits)) - -class Attribute { -public: - Attribute(void); - virtual ~Attribute(void); - static bool InsertByName(Attribute **tree, bool predefined, const wchar_t name[], const wchar_t spacingText[], Symbol subAttribute, int32_t value, wchar_t errMsg[]); - static bool SearchByName(Attribute *tree, wchar_t name[], wchar_t actualName[], Symbol *subAttribute, int32_t *value, wchar_t errMsg[]); - static bool InsertByValue(Attribute **tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[]); - static bool SearchByValue(Attribute *tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[]); - static bool SortByValue(Attribute **to, Attribute *from, wchar_t errMsg[]); -#ifdef DEBUGCVT - static void Dump(Attribute *tree); -#endif -private: - Attribute *left,*right; // binary tree - wchar_t name[cvtAttributeStrgLen],spacingText[cvtAttributeStrgLen]; - bool predefined; - Symbol subAttribute; // group..category - int32_t value; -}; - -Attribute::Attribute(void) { - this->left = this->right = NULL; - this->predefined = false; - this->name[0] = L'\0'; - this->spacingText[0] = L'\0'; - this->subAttribute = eot; - this->value = -1; -} // Attribute::Attribute - -Attribute::~Attribute(void) { - if (this->left) delete this->left; - if (this->right) delete this->right; -} // Attribute::~Attribute - -void AssignString(wchar_t d[], const wchar_t s[], int32_t n) { - int32_t i; - - for (i = 0; i < n && *s; i++) *d++ = *s++; - if (i < n) *d = L'\0'; -} // AssignString - -int32_t CompareString(wchar_t a[], wchar_t b[], int32_t n) { - int32_t i; - - for (i = 0; i < n && *a && *b && *a == *b; a++, b++, i++); - return i == n ? 0 : *a - *b; -} // CompareString - -#define QCap(ch) (((ch) & 0xffdf)) // we know at this point that we compare alpha-numeric strings, hence it's enough to clear bit 5 - -int32_t CompareCapString(const wchar_t a[], const wchar_t b[], int32_t n) { - int32_t i; - - for (i = 0; i < n && *a && *b && QCap(*a) == QCap(*b); a++, b++, i++); - return i == n ? 0 : QCap(*a) - QCap(*b); -} // CompareCapString - -bool Attribute::InsertByName(Attribute **tree, bool predefined, const wchar_t name[], const wchar_t spacingText[], Symbol subAttribute, int32_t value, wchar_t errMsg[]) { - int32_t cmp; - - if (!(*tree)) { - *tree = new Attribute; - if (!(*tree)) { swprintf(errMsg,L"Insufficient memory to define attribute \x22" WIDE_STR_FORMAT L"\x22",name); return false; } - (*tree)->predefined = predefined; - AssignString((*tree)->name,name,cvtAttributeStrgLen); - if (spacingText) AssignString((*tree)->spacingText,spacingText,cvtAttributeStrgLen); - (*tree)->subAttribute = subAttribute; - (*tree)->value = value; - return true; - } else { - cmp = CompareCapString(name,(*tree)->name,cvtAttributeStrgLen); - if (!cmp) { swprintf(errMsg,L"Attribute \x22" WIDE_STR_FORMAT L"\x22 " WIDE_STR_FORMAT L"defined",(*tree)->name,(*tree)->predefined ? L"is pre-" : L"already "); return false; } - return Attribute::InsertByName(cmp < 0 ? &(*tree)->left : &(*tree)->right,predefined,name,spacingText,subAttribute,value,errMsg); - } -} // Attribute::InsertByName - -bool Attribute::SearchByName(Attribute *tree, wchar_t name[], wchar_t actualName[], Symbol *subAttribute, int32_t *value, wchar_t errMsg[]) { - int32_t cmp; - - while (tree) { - cmp = CompareCapString(name,tree->name,cvtAttributeStrgLen); - if (!cmp) { - if (actualName) AssignString(actualName,tree->name,cvtAttributeStrgLen); - *subAttribute = tree->subAttribute; - *value = tree->value; - return true; // found - } - tree = cmp < 0 ? tree->left : tree->right; - } - swprintf(errMsg,L"Attribute \x22" WIDE_STR_FORMAT L"\x22 not defined",name); return false; -} // Attribute::SearchByName - -#define PackKey(subAttribute,value) ((int32_t)(subAttribute) << subAttributeBits | (value)) - -bool Attribute::InsertByValue(Attribute **tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[]) { - int32_t key,thisKey; - - if (!(*tree)) { - *tree = new Attribute; - if (!(*tree)) { swprintf(errMsg,L"Insufficient memory to insert attribute \x22" WIDE_STR_FORMAT L"\x22",name); return false; } - AssignString((*tree)->name,name,cvtAttributeStrgLen); - AssignString((*tree)->spacingText,spacingText,cvtAttributeStrgLen); - (*tree)->subAttribute = subAttribute; - (*tree)->value = value; - return true; - } else { - key = PackKey(subAttribute,value); thisKey = PackKey((*tree)->subAttribute,(*tree)->value); - if (key == thisKey) { swprintf(errMsg,L"Attribute \x22" WIDE_STR_FORMAT L"\x22 already inserted",name); return false; } // not expected by now, though - return Attribute::InsertByValue(key < thisKey ? &(*tree)->left : &(*tree)->right,subAttribute,value,name,spacingText,errMsg); - } -} // Attribute::InsertByValue - -bool Attribute::SearchByValue(Attribute *tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[]) { - int32_t key,thisKey; - - while (tree) { - key = PackKey(subAttribute,value); thisKey = PackKey(tree->subAttribute,tree->value); - if (key == thisKey) { - if (name) AssignString(name,tree->name,cvtAttributeStrgLen); - if (spacingText) AssignString(spacingText,tree->spacingText,cvtAttributeStrgLen); - return true; // found - } - tree = key < thisKey ? tree->left : tree->right; - } - swprintf(errMsg,L"Attribute \x22" WIDE_STR_FORMAT L"\x22 not defined",name); return false; -} // Attribute::SearchByValue - -bool Attribute::SortByValue(Attribute **to, Attribute *from, wchar_t errMsg[]) { -// alternatively, to be a tad more memory efficient, could traverse source tree in prefix, inserting allocated nodes at the end of each visit, -// making sure to have two valid trees at any intermediate stage, for the purpose of standard de-allocation in case of any error. - if (from) { - if (!Attribute::SortByValue(to,from->left,errMsg)) return false; - if (!Attribute::SortByValue(to,from->right,errMsg)) return false; - if (!Attribute::InsertByValue(to,from->subAttribute,from->value,from->name,from->spacingText,errMsg)) return false; - } - return true; // by now -} // Attribute::SortByValue - -#ifdef DEBUGCVT -void Attribute::Dump(Attribute *tree) { - wchar_t out[maxLineSize]; - - if (tree) { - Attribute::Dump(tree->left); - switch (tree->subAttribute) { - case group: swprintf(out,L"%-10s %-32s %-32s %10li\r",keyWord[tree->subAttribute],tree->name,tree->spacingText,tree->value); break; - case color: - case direction: - case category: swprintf(out,L"%-10s %-32s %10li\r",keyWord[tree->subAttribute],tree->name,tree->value); break; - default: swprintf(out,L"*INVALID* %-32s %10li\r",tree->name,tree->value); break; - } - DebugS(out); - Attribute::Dump(tree->right); - } -} // Attribute::Dump -#endif - -// flags' values -#define cvtDefined 1 -#define attributeDefined 2 -#define relativeValue 4 - -typedef struct { -// bool defined,attributeDefined; -// bool relative; - short value; - unsigned short flags; - uint32_t attribute; - short breakPpemSize; - short parent; -// LinearListStruct *delta; -} ControlValue; // 16 bytes (?) - -#define cvtReservedFrom 40 -#define cvtReservedTo 64 - -#define maxCvtCutIns 4L - -typedef struct { - short instructionsOnFromPpemSize,instructionsOnToPpemSize; - short dropOutCtrlOffPpemSize; - short scanCtrlFlags,scanTypeFlags; - short clearTypeCtrlFlag; - short linearAdvanceWidthsFlag; - short fpgmBiasNum; - short numCvtCutIns; - short cvtCutInPpemSize[maxCvtCutIns]; - F26Dot6 cvtCutInValue[maxCvtCutIns]; - bool defined[numSettings]; -} Settings; - -void DefaultSettings(Settings *settings) { - int32_t i; - - settings->instructionsOnFromPpemSize = 8; - settings->instructionsOnToPpemSize = 2047; - settings->dropOutCtrlOffPpemSize = 255; // always on - settings->scanCtrlFlags = 0x100 | settings->dropOutCtrlOffPpemSize; - settings->scanTypeFlags = 5; // smart drop out control - settings->clearTypeCtrlFlag = 0; // not optimized for ClearType - settings->linearAdvanceWidthsFlag = 0; // don't allow fractional ppem sizes - settings->fpgmBiasNum = 0; - settings->numCvtCutIns = 3; - settings->cvtCutInPpemSize[0] = 1; - settings->cvtCutInPpemSize[1] = 29; - settings->cvtCutInPpemSize[2] = 128; - settings->cvtCutInValue[0] = 4*one6; - settings->cvtCutInValue[1] = 3*one6/2; - settings->cvtCutInValue[2] = 0; - for (i = 0; i < numSettings; i++) settings->defined[i] = false; // got defaults only -} // DefaultSettings - -typedef enum { voidParam = 0, naturalN, rationalN, ppemN, rangeOfPpemN, multipleRangesOfPpemN, deltaAtRangeOfPpemN, anyS} ParamType; - -typedef struct { - ParamType type; - int32_t value; - wchar_t *literal; // pointer to scanner's literal for memory efficiency, since we don't have more than 1 string parameter per TMT command - int32_t lowPpemSize,highPpemSize; // in-out for ppemSize related params - bool deltaPpemSize[maxPpemSize]; // here we have possibly more than one rangeOfPpemN parameter, but could implement bit vectors... - DeltaColor deltaColor; // alwaysDelta, blackDelta, greyDelta, ..., same for the entire bit vector deltaPpemSize above -} ActParam; - -typedef struct { - uint32_t attribute; - unsigned short value; // cvt values are biased by 0x8000 - short num; -} CvtKey; - -class PrivateControlValueTable : public ControlValueTable { -public: - PrivateControlValueTable(void); - virtual ~PrivateControlValueTable(void); - virtual bool Compile(TextBuffer*source, TextBuffer*prepText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]); - virtual bool IsControlProgramFormat(void); - virtual bool LinearAdvanceWidths(void); - virtual int32_t LowestCvtNum(void); - virtual int32_t HighestCvtNum(void); - virtual int32_t LowestCvtIdx(void); - virtual int32_t HighestCvtIdx(void); - virtual int32_t CvtNumOf(int32_t idx); - virtual int32_t CvtIdxOf(int32_t num); - virtual bool CvtNumExists(int32_t cvtNum); - virtual bool GetCvtValue(int32_t cvtNum, short *cvtValue); - virtual bool CvtAttributesExist(int32_t cvtNum); // entered a cvt "comment"? - virtual bool GetCvtAttributes(int32_t cvtNum, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory, bool *relative); - virtual int32_t NumCharGroups(void); - virtual bool GetAttributeStrings(int32_t cvtNum, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[], wchar_t relative[]); - virtual bool GetCharGroupString(CharGroup group, wchar_t string[]); - virtual bool GetSpacingText(CharGroup group, wchar_t spacingText[]); - virtual int32_t GetBestCvtMatch(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory, int32_t distance); // returns invalidCvtNum if no match - virtual void PutCvtBinary(int32_t size, unsigned char data[]); - virtual void GetCvtBinary(int32_t *size, unsigned char data[]); - virtual int32_t GetCvtBinarySize(void); - virtual uint32_t PackAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory); - virtual void UnpackAttribute(uint32_t attribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory); - virtual void UnpackAttributeStrings(uint32_t attribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]); - virtual bool DumpControlValueTable(TextBuffer *text); - virtual bool CompileCharGroup(File *from, short platformID, unsigned char toCharGroupOfCharCode[], wchar_t errMsg[]); -private: - Scanner scanner; - TTEngine *tt; - Attribute *attributes,*tempAttributes; - bool oldSyntax,newSyntax; - bool legacyCompile; - wchar_t *errMsg; - bool cvtDataValid,cvtDataSorted; - int32_t lowestCvtNum,highestCvtNum; - int32_t lowestCvtIdx,highestCvtIdx; - int32_t newNumCharGroups; - Settings cpgmSettings,tempSettings; - ControlValue *cpgmData,*tempData; - CvtKey cvtKeyOfIdx[1 + maxCvtNum + 1]; // cvt key sorted by cvtAttribute and cvtValue, with sentinels at either end - short cvtIdxOfNum[maxCvtNum]; // inverse table of above, cvtIdx[cvtNum[idx]] = idx - bool AttributeDeclaration(int32_t firstAvailSubAttributeValue[]); - bool SettingsDeclaration(void); - bool CvtDeclaration(uint32_t *attribute); - bool AttributeAssociation(uint32_t *attribute); - bool ValueAssociation(uint32_t attribute, int32_t *cvtNum, ControlValue **cvt); - bool InheritanceRelation(int32_t cvtNum, ControlValue *cvt); - bool DeltaDeclaration(int32_t cvtNum, ControlValue *cvt); - bool InlineSttmt(void); - bool Parameter(ActParam *actParam); - bool Expression(ActParam *actParam); - bool Term(ActParam *actParam); - bool Factor(ActParam *actParam); - bool PixelAtPpemRange(DeltaColor cmdColor, ActParam *actParam, DeltaColor *paramColor); - bool PpemRange(ActParam *actParam); - bool Range(ActParam *actParam); - void AssertSortedCvt(void); - void SortCvtKeys(int32_t low, int32_t high); -}; - -ControlValue *NewCvtData(void) { - int32_t cvtNum; - ControlValue *cvt,*cvtData; - - cvtData = (ControlValue *)NewP(((int32_t)maxCvtNum)*((int32_t)sizeof(ControlValue))); // all these parens appear to be necessary to convice the vc compiler that the operation should be carried out in int32_t... - if (!cvtData) return NULL; - for (cvt = cvtData, cvtNum = 0; cvtNum < maxCvtNum; cvt++, cvtNum++) { - cvt->flags = 0; - cvt->value = 0; - cvt->attribute = 0; - cvt->breakPpemSize = 0; - cvt->parent = invalidCvtNum; - // cvt->delta = NULL; - } - return cvtData; -} // NewCvtData - -void DisposeCvtData(ControlValue **cvtData) { -// int32_t cvtNum; -// ControlValue *cvt; - - if (!(*cvtData)) return; -// for (cvt = *cvtData, cvtNum = 0; cvtNum < maxCvtNum; cvt++, cvtNum++) { -// if (cvt->delta) ListStruct::Delete((ListStruct **)&cvt->delta); -// } - DisposeP((void **)cvtData); -} // DisposeCvtData - -PrivateControlValueTable::PrivateControlValueTable(void) { - this->legacyCompile = false; - this->tt = NULL; - this->cvtDataValid = this->cvtDataSorted = false; - this->lowestCvtNum = maxCvtNum; - this->highestCvtNum = -1; - this->attributes = this->tempAttributes = NULL; - DefaultSettings(&this->cpgmSettings); - this->cpgmData = NewCvtData(); - this->tempData = NULL; -} // PrivateControlValueTable::PrivateControlValueTable - -PrivateControlValueTable::~PrivateControlValueTable(void) { - if (this->cpgmData) DisposeCvtData(&this->cpgmData); - if (this->attributes) delete this->attributes; -// CvtDelta::Flush(); -} // PrivateControlValueTable::~PrivateControlValueTable - -#define WhiteSpace(scanner) (L'\0' < scanner->ch && scanner->ch <= L' ' /* && scanner->ch != '\r' */) -#define InitComment(scanner) (scanner->ch == L'/' && scanner->ch2 == L'*') -#define TermComment(scanner) (scanner->ch == L'*' && scanner->ch2 == L'/') -#define Numeric(ch) (L'0' <= (ch) && (ch) <= L'9') -#define Alpha(ch) ((L'A' <= (ch) && (ch) <= L'Z') || (L'a' <= (ch) && (ch) <= L'z')) -#define shortMax 32767L -#define hShortMax 65535L -#define shortMin -32768L - -void Scanner::GetCh(void) { - this->ch = this->ch2; - this->ch2 = this->text && this->pos < this->len ? this->text[this->pos] : L'\0'; -// this->ch2 = Cap(this->ch2); // don't make it case insensitive for now - this->pos++; -} // Scanner::GetCh - -bool Scanner::SkipComment(void) { - int32_t commentPos = this->pos; - - this->GetCh(); this->GetCh(); - while (this->ch && !TermComment(this)) { - if (InitComment(this)) this->SkipComment(); else this->GetCh(); - } - if (this->ch) { // TermComment - this->GetCh(); this->GetCh(); - } else { - this->symPos = commentPos; swprintf(this->errMsg,L"Comment opened but not closed"); return false; - } - return true; -} // Scanner::SkipComment - -bool Scanner::Skip(void) { - while (WhiteSpace(this) || InitComment(this)) { - if (this->ch <= L' ') - this->GetCh(); - else if (!this->SkipComment()) - return false; - } - return true; -} // Scanner::Skip - -bool Scanner::Init(TextBuffer*source, File *file, wchar_t errMsg[]) { - int32_t i; - size_t textLen; - - this->prevSymPos = this->prevSymEnd = this->symPos = -1; - this->source = source; - this->file = file; - this->errMsg = errMsg; // copy pointer - this->pos = 0; - if (this->source) - { - this->len = source->TheLength(); - this->text = (wchar_t *)NewP((this->len + 1) * sizeof(wchar_t)); // '\0'... - if (!this->text) return false; - - source->GetText(&textLen,this->text); - } - else - { - file->ReadUnicode(&this->len, &this->text); - } - this->text[this->len] = L'\0'; - this->ch2 = 0; // silence BC - for (i = 0; i < lookahead; i++) this->GetCh(); - return this->GetSym(); -} // Scanner::Init - -void Scanner::Term(int32_t *errPos, int32_t *errLen) { - *errPos = this->symPos - lookahead; - *errLen = this->pos - this->symPos; - if (this->text) DisposeP((void **)(&this->text)); -} // Scanner::Term - -bool Scanner::GetNum(void) { - int32_t digit,decPlcs,pwrOf10; - - this->value = 0; - if (this->ch == L'0' && Cap(this->ch2) == L'X') { - this->GetCh(); this->GetCh(); this->ch = Cap(this->ch); - while (Numeric(this->ch) || (L'A' <= this->ch && this->ch <= L'F')) { - digit = this->ch <= L'9' ? (int32_t)this->ch - (int32_t)'0' : (int32_t)this->ch - (int32_t)'A' + 10; - if (this->value*16 + digit > hShortMax) { swprintf(this->errMsg,L"Hexadecimal number too large"); return false; } - this->value = this->value*16 + digit; - this->GetCh(); this->ch = Cap(this->ch); - } - this->sym = hexadecimal; - } else { - while (Numeric(this->ch)) { - digit = (int32_t)this->ch - (int32_t)'0'; - if (this->value*10 + digit > shortMax) { swprintf(this->errMsg,L"Number too large"); return false; } - this->value = this->value*10 + digit; - this->GetCh(); - } - this->sym = natural; // so far - if (this->ch == '.' && this->ch2 != '.') { // distinguish from ellipsis - this->GetCh(); - decPlcs = 0; pwrOf10 = 1; - while (Numeric(this->ch)) { - digit = (int32_t)this->ch - (int32_t)'0'; - if (decPlcs*10 * digit > 1000000L) { swprintf(this->errMsg,L"Too many decimal places"); return false; } // 1/64 = 0.015625 - decPlcs = 10*decPlcs + digit; pwrOf10 *= 10L; - this->GetCh(); - } - if (pwrOf10 > 1) { - this->value = this->value*one6 + (decPlcs*one6 + pwrOf10/2)/pwrOf10; - this->sym = rational; // by now - } - } - } - return true; // by now -} // Scanner::GetNum - -bool Scanner::GetIdent(void) { - int32_t i; - - i = 0; - while (Alpha(this->ch) || Numeric(this->ch)) { - if (i >= cvtAttributeStrgLen) - { - swprintf(this->errMsg,L"Identifier too long (cannot have more than %i characters)",(int32_t)cvtAttributeStrgLen); return false; - } - this->literal[i++] = this->ch; - this->GetCh(); - } - if (i < cvtAttributeStrgLen) this->literal[i] = L'\0'; - if (this->source) { - for (i = 0; i < numKeyWords && CompareCapString(this->literal,(wchar_t *)keyWord[i],cvtAttributeStrgLen); i++); // linear search, there are only a few keywords - this->sym = (Symbol)i; - if (this->sym == color || this->sym == direction) this->sym = ident; // don't allow user to define these, so far at least... - if (this->sym < ident && CompareString(this->literal,(wchar_t *)keyWord[this->sym],cvtAttributeStrgLen)) this->ReplaceIdent((wchar_t *)keyWord[this->sym]); - } else { // compiling character group, there are no reserved words... - this->sym = ident; - } - return true; // by now -} // Scanner::GetIdent - -bool Scanner::GetLiteral(void) { - int32_t i; - - this->GetCh(); - i = 0; - while (this->ch && this->ch != L'"') { - if (i >= maxAsmSize-1) { swprintf(this->errMsg,L"String too long (cannot be longer than %li characters)",maxAsmSize-1); return true; } - this->literal[i++] = this->ch; - this->GetCh(); - } - this->literal[i++] = L'\0'; - if (!this->ch) { swprintf(this->errMsg,L"\x22 expected"); return false; } - this->GetCh(); - this->sym = ::literal; - return true; // by now -} // Scanner::GetLiteral - -bool Scanner::GetSym(void) { - this->sym = eot; - this->prevSymEnd = this->pos; - if (!this->Skip()) return false; - this->prevSymPos = this->symPos; this->symPos = this->pos; - if (Numeric(this->ch)) { - return this->GetNum(); - } else if (Alpha(this->ch)) { - return this->GetIdent(); - } else { - switch (this->ch) { - case L'"' : return this->GetLiteral(); - case L'.' : this->GetCh(); if (this->ch == L'.') { this->GetCh(); this->sym = ellipsis; } else this->sym = period; break; - case L',' : this->GetCh(); this->sym = comma; break; - case L':' : this->GetCh(); this->sym = colon; break; - case L';' : this->GetCh(); this->sym = semiColon; break; - case L'%' : this->GetCh(); this->sym = percent; break; - case L'@' : this->GetCh(); this->sym = at; break; - case L'(' : this->GetCh(); this->sym = leftParen; break; - case L'[' : this->GetCh(); this->sym = leftParen; this->ReplaceIdent(L"("); break; - case L')' : this->GetCh(); this->sym = rightParen; break; - case L']' : this->GetCh(); this->sym = rightParen; this->ReplaceIdent(L")"); break; - case L'+' : this->GetCh(); this->sym = plus; break; - case L'-' : this->GetCh(); this->sym = minus; break; - case L'*' : this->GetCh(); this->sym = times; break; - case L'/' : this->GetCh(); this->sym = divide; break; - case L'=' : this->GetCh(); this->sym = equals; break; - case L'~' : this->GetCh(); this->sym = relatesTo; break; - // case L'\r': this->GetCh(); this->sym = eol; break; - case L'\0': this->sym = eot; break; - default : this->GetCh(); swprintf(this->errMsg,L"Invalid character in control value table"); return false; break; - } - } - return true; -} // Scanner::GetSym - -void Scanner::ReplaceIdent(const wchar_t capIdent[]) { - int32_t beg,end; - - beg = this->symPos - lookahead; - end = beg + (int32_t)STRLENW(capIdent); - this->source->Delete(beg,end); - this->source->Insert(beg,capIdent); -} // Scanner::ReplaceIdent - -void Scanner::ErrUnGetSym(void) { - this->pos = this->prevSymEnd; this->symPos = this->prevSymPos; // after semantical/contextual error, we're one symbol ahead, hence retract for correct error high-lighting -} // Scanner::ErrUnGetSym - -bool AssertNatural(ActParam *actParam, int32_t low, int32_t high, const wchar_t name[], wchar_t errMsg[]) { - if (actParam->type != naturalN) { swprintf(errMsg,WIDE_STR_FORMAT L" expected (must be an integer in range %li through %li)",name,low,high); return false; } - actParam->value >>= places6; - if (actParam->value < low || high < actParam->value) { swprintf(errMsg,WIDE_STR_FORMAT L" out of range (must be in range %li through %li)",name,low,high); return false; } - return true; // by now -} // AssertNatural - -bool AssertPixelAmount(ActParam *actParam, F26Dot6 low, F26Dot6 high, const wchar_t name[], wchar_t errMsg[]) { - if (actParam->type == naturalN) actParam->type = rationalN; - if (actParam->type != rationalN) { swprintf(errMsg,WIDE_STR_FORMAT L" expected (must be a pixel amount in range %8.6f through %8.6f)",name,(double)low/one6,(double)high/one6); return false; } - if (actParam->value < low || high < actParam->value) { swprintf(errMsg,WIDE_STR_FORMAT L" expected (must be in range %8.6f through %8.6f)",name,(double)low/one6,(double)high/one6); return false; } - return true; // by now -} // AssertPixelAmount - -bool PrivateControlValueTable::AttributeDeclaration(int32_t firstAvailSubAttributeValue[]) { - Symbol sym; - wchar_t name[cvtAttributeStrgLen],spacingText[cvtAttributeStrgLen]; - - this->newSyntax = true; - sym = this->scanner.sym; - if (!this->scanner.GetSym()) return false; - if (this->scanner.sym != ident) { swprintf(errMsg,WIDE_STR_FORMAT L" name expected",keyWord[sym]); return false; } - if (firstAvailSubAttributeValue[sym] >= maxSubAttributes) { swprintf(errMsg,WIDE_STR_FORMAT L" name exceeds capacity (cannot have more than %li)",keyWord[sym],maxSubAttributes); return false; } - AssignString(name,this->scanner.literal,cvtAttributeStrgLen); - if (!this->scanner.GetSym()) return false; - spacingText[0] = L'\0'; - if (sym == group && this->scanner.sym == literal) { - AssignString(spacingText,this->scanner.literal,cvtAttributeStrgLen); - if (!this->scanner.GetSym()) return false; - } - if (!Attribute::InsertByName(&this->tempAttributes,false,name,spacingText,sym,firstAvailSubAttributeValue[sym],errMsg)) { this->scanner.ErrUnGetSym(); return false; } - firstAvailSubAttributeValue[sym]++; - return true; // by now - -} // PrivateControlValueTable::AttributeDeclaration - -bool PrivateControlValueTable::SettingsDeclaration(void) { - Symbol sym; - ActParam instrOnParam,dropOffParam,scanCtrlParam,scanTypeParam,cvtCutInPixelSizeParam,cvtCutInPpemSizeParam,clearTypeCtrlParam,linearAdvanceWidthsParam,fpgmBiasParam; - wchar_t comment[maxLineSize]; - - this->newSyntax = true; - sym = this->scanner.sym; - if (this->tempSettings.defined[sym-firstSetting]) { swprintf(this->errMsg,WIDE_STR_FORMAT L" already defined",keyWord[sym]); return false; } - if (!this->scanner.GetSym()) return false; - - if (this->legacyCompile || sym != fpgmBias) { - swprintf(comment,L"/* " WIDE_STR_FORMAT L" */",keyWord[sym]); this->tt->Emit(comment); - } - - switch (sym) { - case instructionsOn: - instrOnParam.lowPpemSize = 0; instrOnParam.highPpemSize = shortMax; - if (!this->Parameter(&instrOnParam)) return false; - if (instrOnParam.type != rangeOfPpemN) { swprintf(this->errMsg,L"Range of ppem sizes at which instructions are on expected (Example: @8..2047 to activate instructions in range 8 through 2047 ppem)"); this->scanner.ErrUnGetSym(); return false; } - this->tempSettings.instructionsOnFromPpemSize = (short)instrOnParam.value; - this->tempSettings.instructionsOnToPpemSize = (short)instrOnParam.lowPpemSize; - this->tt->INSTCTRL(this->tempSettings.instructionsOnFromPpemSize,this->tempSettings.instructionsOnToPpemSize); - this->tempSettings.defined[sym - firstSetting] = true; - break; - case dropOutCtrlOff: - if (this->tempSettings.defined[scanCtrl-firstSetting] || this->tempSettings.defined[scanType-firstSetting]) { swprintf(this->errMsg,L"Cannot use " WIDE_STR_FORMAT L" together with " WIDE_STR_FORMAT L" or " WIDE_STR_FORMAT,keyWord[sym],keyWord[scanCtrl],keyWord[scanType]); this->scanner.ErrUnGetSym(); return false; } - dropOffParam.lowPpemSize = -1; dropOffParam.highPpemSize = maxPpemSize-1; // lowest permissible ppem size - 1 - if (!this->Parameter(&dropOffParam)) return false; - if (dropOffParam.type != ppemN) { swprintf(this->errMsg,L"Drop-out control turn-off ppem size expected (must be an integer in range @%li through @%li)" BRK L"Drop-out control turn-off ppem size specifies the ppem size at and above which drop-out control is no longer turned on.",1,dropOffParam.highPpemSize); this->scanner.ErrUnGetSym(); return false; } - this->tempSettings.dropOutCtrlOffPpemSize = (short)dropOffParam.value; - this->tempSettings.scanCtrlFlags = (this->tempSettings.scanCtrlFlags & 0xff00) | this->tempSettings.dropOutCtrlOffPpemSize; - this->tt->SCANCTRL(this->tempSettings.scanCtrlFlags); - this->tt->SCANTYPE(this->tempSettings.scanTypeFlags); - this->tempSettings.defined[sym - firstSetting] = true; - break; - case scanCtrl: - if (this->tempSettings.defined[dropOutCtrlOff-firstSetting]) { swprintf(this->errMsg,L"Cannot use " WIDE_STR_FORMAT L" together with " WIDE_STR_FORMAT,keyWord[sym],keyWord[dropOutCtrlOff]); this->scanner.ErrUnGetSym(); return false; } - if (this->scanner.sym != equals) { swprintf(this->errMsg,L"= expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&scanCtrlParam)) return false; - if (!AssertNatural(&scanCtrlParam,0,16383,L"Value for scan control",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } // bits 14 and 15 reserved for future use - this->tempSettings.scanCtrlFlags = (short)scanCtrlParam.value; - this->tt->SCANCTRL(this->tempSettings.scanCtrlFlags); - this->tempSettings.defined[sym - firstSetting] = true; - break; - case scanType: - if (this->tempSettings.defined[dropOutCtrlOff-firstSetting]) { swprintf(this->errMsg,L"Cannot use " WIDE_STR_FORMAT L" together with " WIDE_STR_FORMAT,keyWord[sym],keyWord[dropOutCtrlOff]); return false; } - if (this->scanner.sym != equals) { swprintf(this->errMsg,L"= expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&scanTypeParam)) return false; - if (!AssertNatural(&scanTypeParam,1,6,L"Value for scan type",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - this->tempSettings.scanTypeFlags = (short)scanTypeParam.value; - this->tt->SCANTYPE(this->tempSettings.scanTypeFlags); - this->tempSettings.defined[sym - firstSetting] = true; - break; - case cvtCutIn: - if (this->scanner.sym != equals) { swprintf(this->errMsg,L"= expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&cvtCutInPixelSizeParam)) return false; - if (!AssertPixelAmount(&cvtCutInPixelSizeParam,0,maxPixelValue,L"Cut-in pixel amount",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - this->tempSettings.cvtCutInValue[0] = cvtCutInPixelSizeParam.value; - cvtCutInPpemSizeParam.lowPpemSize = 0; cvtCutInPpemSizeParam.highPpemSize = maxPpemSize-1; - this->tempSettings.numCvtCutIns = 1; - while (this->scanner.sym == comma) { - if (this->tempSettings.numCvtCutIns >= maxCvtCutIns) { swprintf(this->errMsg,L"Too many cvt cut-ins (cannot have more than %li)",maxCvtCutIns); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&cvtCutInPixelSizeParam)) return false; - if (!AssertPixelAmount(&cvtCutInPixelSizeParam,0,this->tempSettings.cvtCutInValue[this->tempSettings.numCvtCutIns-1]-1,L"Cut-in pixel amount",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } // allow at most 1/64 less than preceding cut in - if (!this->Parameter(&cvtCutInPpemSizeParam)) return false; - this->tempSettings.cvtCutInValue[this->tempSettings.numCvtCutIns] = cvtCutInPixelSizeParam.value; - this->tempSettings.cvtCutInPpemSize[this->tempSettings.numCvtCutIns] = (short)cvtCutInPpemSizeParam.value; - this->tempSettings.numCvtCutIns++; - } - this->tt->AssertFreeProjVector(yRomanDir); // so far, this may become aspect-ratio dependent, or such like... - this->tt->SCVTCI(this->tempSettings.numCvtCutIns,this->tempSettings.cvtCutInPpemSize,this->tempSettings.cvtCutInValue); - this->tempSettings.defined[sym - firstSetting] = true; - break; - default: - break; - } - - if (!this->legacyCompile) - { - switch (sym) - { - case clearTypeCtrl: - if (this->scanner.sym != equals) { swprintf(this->errMsg, L"= expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&clearTypeCtrlParam)) return false; - if (!AssertNatural(&clearTypeCtrlParam, 0, 1, L"Value for ClearTypeCtrl", this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - this->tempSettings.clearTypeCtrlFlag = (short) clearTypeCtrlParam.value; - this->tt->SetClearTypeCtrl(this->tempSettings.clearTypeCtrlFlag); - this->tempSettings.defined[sym - firstSetting] = true; - break; - case linearAdvanceWidths: - if (this->scanner.sym != equals) { swprintf(this->errMsg, L"= expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&linearAdvanceWidthsParam)) return false; - if (!AssertNatural(&linearAdvanceWidthsParam, 0, 1, L"Value for linearAdvanceWidths", this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - this->tempSettings.linearAdvanceWidthsFlag = (short) linearAdvanceWidthsParam.value; - // client to inquire flag after CVT compilation and to call font->UpdateAdvanceWidthFlag - this->tempSettings.defined[sym - firstSetting] = true; - break; - case fpgmBias: - if (this->scanner.sym != equals) { swprintf(this->errMsg, L"= expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&fpgmBiasParam)) return false; - if (!AssertNatural(&fpgmBiasParam, 0, 32767, L"Value for FPgmBias", this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - this->tempSettings.fpgmBiasNum = (short) fpgmBiasParam.value; - this->tt->SetFunctionNumberBias(this->tempSettings.fpgmBiasNum); - this->tempSettings.defined[sym - firstSetting] = true; - break; - default: - break; - } - } - - return true; // by now -} // PrivateControlValueTable::SettingsDeclaration - -bool PrivateControlValueTable::CvtDeclaration(uint32_t *attribute) { - int32_t cvtNum; - ControlValue *cvt = nullptr; - - if (!(this->AttributeAssociation(attribute) && // this->InlineSttmt() && - this->ValueAssociation(*attribute,&cvtNum,&cvt) && this->InlineSttmt() && - this->InheritanceRelation(cvtNum,cvt) && this->InlineSttmt() && - this->DeltaDeclaration(cvtNum,cvt) && this->InlineSttmt())) return false; - cvt->flags |= cvtDefined; // at the very end only, or else we would allow referencing self as a parent... - return true; // by now -} // PrivateControlValueTable::CvtDeclaration - -bool PrivateControlValueTable::AttributeAssociation(uint32_t *attribute) { - CharGroup currGroup; - LinkColor currColor; - LinkDirection currDirection; - CvtCategory currCategory; - Symbol subAttribute; - int32_t value; - wchar_t actualName[cvtAttributeStrgLen]; - - PrivateControlValueTable::UnpackAttribute(*attribute,&currGroup,&currColor,&currDirection,&currCategory); - while (this->scanner.sym == ident) { - this->newSyntax = true; - if (!Attribute::SearchByName(this->tempAttributes,this->scanner.literal,actualName,&subAttribute,&value,this->errMsg)) return false; - if (CompareString(this->scanner.literal,actualName,cvtAttributeStrgLen)) this->scanner.ReplaceIdent(actualName); - switch (subAttribute) { - case group: currGroup = (CharGroup)value; break; - case color: currColor = (LinkColor)value; break; - case direction: currDirection = (LinkDirection)value; break; - case category: currCategory = (CvtCategory)value; break; - default: break; - } - if (!this->scanner.GetSym()) return false; - if (!this->InlineSttmt()) return false; - } - *attribute = PrivateControlValueTable::PackAttribute(currGroup,currColor,currDirection,currCategory); - return true; // by now -} // PrivateControlValueTable::AttributeAssociation - -bool PrivateControlValueTable::ValueAssociation(uint32_t attribute, int32_t *cvtNum, ControlValue **cvt) { - ActParam cvtNumParam,cvtValueParam; - CharGroup charGroup; - LinkColor linkColor; - LinkDirection linkDirection; - CvtCategory cvtCategory; - - if (!this->Parameter(&cvtNumParam)) return false; - if (!AssertNatural(&cvtNumParam,0,maxCvtNum-1,L"Cvt number",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - *cvtNum = cvtNumParam.value; -// we're currently not testing this as there may be users with different fpgms and we should really un-hardwire the remaining hard-wired cvts -// if (cvtReservedFrom <= *cvtNum && *cvtNum <= cvtReservedTo) { swprintf(this->errMsg,L"Cvt numbers in range %i through %i are reserved",(int32_t)cvtReservedFrom,(int32_t)cvtReservedTo); this->scanner.ErrUnGetSym(); return false; } - *cvt = &this->tempData[cvtNumParam.value]; - if ((*cvt)->flags & cvtDefined) { swprintf(this->errMsg,L"cvt number already defined"); this->scanner.ErrUnGetSym(); return false; } - if (this->scanner.sym != colon) { swprintf(this->errMsg,L"':' expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&cvtValueParam)) return false; - if (cvtValueParam.type != naturalN) { swprintf(this->errMsg,L"Cvt value expected (must be an integer specifying font design units)"); this->scanner.ErrUnGetSym(); return false; } - cvtValueParam.value >>= places6; - (*cvt)->value = (short)cvtValueParam.value; - if (this->scanner.sym == hexadecimal) { - if (this->newSyntax) { swprintf(this->errMsg,L"Cannot mix cvt formats (hexadecimal attributes are used in the old cvt format only)"); return false; } - this->oldSyntax = true; - UnpackCvtHexAttribute((short)this->scanner.value,&charGroup,&linkColor,&linkDirection,&cvtCategory); - (*cvt)->attribute = PrivateControlValueTable::PackAttribute(charGroup,linkColor,linkDirection,cvtCategory); - (*cvt)->flags |= attributeDefined; - if (!this->scanner.GetSym()) return false; - } else if (this->newSyntax) { - (*cvt)->attribute = attribute; - (*cvt)->flags |= attributeDefined; - } -// (*cvt)->flags |= PostSizeToViewTitle; not yet - return true; // by now -} // PrivateControlValueTable::ValueAssociation - -bool PrivateControlValueTable::InheritanceRelation(int32_t cvtNum, ControlValue *cvt) { - bool relative; - ActParam parentCvtNumParam,ppemValueParam; - ControlValue *parentCvt; - CharGroup charGroup; - LinkColor linkColor; - LinkDirection linkDirection; - CvtCategory cvtCategory; - - if (this->scanner.sym == equals || this->scanner.sym == relatesTo) { - relative = this->scanner.sym == relatesTo; - this->newSyntax = true; - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&parentCvtNumParam)) return false; - if (!AssertNatural(&parentCvtNumParam,0,maxCvtNum-1,L"Parent cvt number",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - parentCvt = &this->tempData[parentCvtNumParam.value]; - if (!(parentCvt->flags & cvtDefined)) { swprintf(this->errMsg,L"Parent cvt not defined (must be completely defined prior to tying child cvts to it)"); this->scanner.ErrUnGetSym(); return false; } -// if (this->scanner.sym != at) { swprintf(this->errMsg,L"'@' expected"); return false; } -// if (!this->scanner.GetSym()) return false; - if (!this->legacyCompile) { - ppemValueParam.lowPpemSize = 0; // lowest permissible ppem size - 1 - } else { - ppemValueParam.lowPpemSize = parentCvt->breakPpemSize; // lowest permissible ppem size - 1 - } - - ppemValueParam.highPpemSize = maxPpemSize-1; - if (!this->Parameter(&ppemValueParam)) return false; - if (ppemValueParam.type != ppemN) { swprintf(this->errMsg,L"Break ppem size expected (must be an integer in range @%li through @%li)" BRK L"The break ppem size specifies the ppem size at which this child cvt is no longer tied to its parent.",parentCvt->breakPpemSize+1,ppemValueParam.highPpemSize); this->scanner.ErrUnGetSym(); return false; } - if (this->legacyCompile){ - if (ppemValueParam.value <= parentCvt->breakPpemSize || ppemValueParam.highPpemSize < ppemValueParam.value) { swprintf(this->errMsg, L"Break ppem size out of range (must be in range @%li through @%li)" BRK L"The break ppem size must be above the break ppem size of the parent of this child cvt)", parentCvt->breakPpemSize + 1, ppemValueParam.highPpemSize); this->scanner.ErrUnGetSym(); return false; } - } - - cvt->parent = (short)parentCvtNumParam.value; - cvt->breakPpemSize = (short)ppemValueParam.value; - if (relative) cvt->flags |= relativeValue; - this->UnpackAttribute(cvt->attribute,&charGroup,&linkColor,&linkDirection,&cvtCategory); - this->tt->AssertFreeProjVector(linkDirection == linkX ? xRomanDir : yRomanDir); - this->tt->CvtRegularization(relative,(short)cvtNum,cvt->breakPpemSize,cvt->parent); - cvt->attribute = this->PackAttribute(charGroup,linkColor,linkDirection,cvtCategory); - } - return true; // by now -} // PrivateControlValueTable::InheritanceRelation - -bool PrivateControlValueTable::DeltaDeclaration(int32_t cvtNum, ControlValue *cvt) { - ActParam pixelAtPpemRangeParam; - bool colorDeltaDone[numDeltaColors]; - DeltaColor cmdColor,paramColor; - int32_t i; - CharGroup charGroup; - LinkColor linkColor; - LinkDirection linkDirection; - CvtCategory cvtCategory; - - for (i = 0; i < numDeltaColors; i++) colorDeltaDone[i] = false; - while (cvtDelta <= this->scanner.sym && this->scanner.sym <= gCvtDelta) { - this->newSyntax = true; - cmdColor = (DeltaColor)(this->scanner.sym-cvtDelta); - if (colorDeltaDone[cmdColor]) { swprintf(this->errMsg,L"Cannot have more than one " WIDE_STR_FORMAT L" command per control value" BRK \ - L"Please combine them to a single " WIDE_STR_FORMAT L" command. Example: " WIDE_STR_FORMAT L"(1 @18..20;22, -1 @ 24..25)", - keyWord[this->scanner.sym],keyWord[this->scanner.sym],keyWord[this->scanner.sym]); return false; } - colorDeltaDone[cmdColor] = true; - if (!this->scanner.GetSym()) return false; - if (this->scanner.sym != leftParen) { swprintf(this->errMsg,L"( expected"); return false; } - if (!this->scanner.GetSym()) return false; - pixelAtPpemRangeParam.lowPpemSize = cvt->breakPpemSize-1; // lowest permissible ppem size - 1 - pixelAtPpemRangeParam.highPpemSize = maxPpemSize-1; - if (!this->PixelAtPpemRange(cmdColor,&pixelAtPpemRangeParam,¶mColor)) return false; -// if (!AppendDeltas(cmdColor,&pixelAtPpemRangeParam,cvt,this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - this->UnpackAttribute(cvt->attribute,&charGroup,&linkColor,&linkDirection,&cvtCategory); - this->tt->AssertFreeProjVector(linkDirection == linkX ? xRomanDir : yRomanDir); - this->tt->DLT(true,paramColor,(short)cvtNum,pixelAtPpemRangeParam.value,pixelAtPpemRangeParam.deltaPpemSize); - while (this->scanner.sym == comma) { - if (!this->scanner.GetSym()) return false; - for (i = maxPpemSize-1; i > 0 && !pixelAtPpemRangeParam.deltaPpemSize[i]; i--); - pixelAtPpemRangeParam.lowPpemSize = i; // lowest permissible ppem size - 1 - if (!this->PixelAtPpemRange(cmdColor,&pixelAtPpemRangeParam,¶mColor)) return false; -// if (!AppendDeltas(cmdColor,&pixelAtPpemRangeParam,cvt,this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - this->tt->DLT(true,paramColor,(short)cvtNum,pixelAtPpemRangeParam.value,pixelAtPpemRangeParam.deltaPpemSize); - } - if (this->scanner.sym != rightParen) { swprintf(this->errMsg,L") expected"); return false; } - if (!this->scanner.GetSym()) return false; - } - return true; // by now -} // PrivateControlValueTable::DeltaDeclaration - -bool PrivateControlValueTable::InlineSttmt(void) { - ActParam asmCodeParam; - - while (this->scanner.sym == asM) { - if (!this->scanner.GetSym()) return false; - if (this->scanner.sym != leftParen) { swprintf(this->errMsg,L"( expected"); return false; } - if (!this->scanner.GetSym()) return false; - if (!this->Parameter(&asmCodeParam)) return false; - if (asmCodeParam.type != anyS) { swprintf(this->errMsg,L"Actual TrueType code expected (Example: \x22/* Comment */\x22)"); this->scanner.ErrUnGetSym(); return false; } - this->tt->Emit(this->scanner.literal); - if (this->scanner.sym != rightParen) { swprintf(this->errMsg,L") expected"); return false; } - if (!this->scanner.GetSym()) return false; - } - return true; // by now -} // PrivateControlValueTable::InlineSttmt - -bool PrivateControlValueTable::Parameter(ActParam *actParam) { - if ((natural <= this->scanner.sym && this->scanner.sym <= rational) || (leftParen <= this->scanner.sym && this->scanner.sym <= minus)) { - if (!this->Expression(actParam)) return false; - } else if (this->scanner.sym == at) { - if (!this->scanner.GetSym()) return false; - if (!this->PpemRange(actParam)) return false; - } else if (this->scanner.sym == literal) { - actParam->type = anyS; actParam->literal = this->scanner.literal; - if (!this->scanner.GetSym()) return false; - } else { - swprintf(this->errMsg,L"parameter starts with invalid symbol (+, -, @, number, or \x22string\x22 expected)"); return false; - } - return true; // by now -} // PrivateControlValueTable::Parameter - -bool ValidBinaryOperation(ActParam *a, ActParam *b, Symbol op, wchar_t errMsg[]) { - wchar_t opName[4][10] = {L"add",L"subtract",L"multiply",L"divide"}; - - if (a->type < naturalN || rationalN < a->type || b->type < naturalN || rationalN < b->type) { - swprintf(errMsg,L"cannot " WIDE_STR_FORMAT L" these operands",opName[op-plus]); return false; - } - a->type = Max(a->type,b->type); - if (op == divide && a->type == naturalN && b->type == naturalN && b->value != 0 && a->value % b->value != 0) a->type = rationalN; - switch (op) { - case plus: - case minus: - if (op == plus) a->value += b->value; else a->value -= b->value; - if (a->value < shortMin << places6) { - swprintf(errMsg,L"result of subtraction too small (cannot be below %li)",shortMin); return false; - } else if (shortMax << places6 < a->value) { - swprintf(errMsg,L"result of addition too large (cannot be above %li)",shortMax); return false; - } - break; - case times: - if ((double)Abs(a->value)*(double)Abs(b->value) < (double)((shortMax + 1) << (places6 + places6))) { - a->value = (a->value*b->value + half6) >> places6; - } else { - swprintf(errMsg,L"result of multiplication too large (cannot be %li or larger in magnitude)",shortMax+1); return false; - } - break; - case divide: - if (b->value != 0 && (double)Abs(a->value) < (double)(shortMax+1)*(double)Abs(b->value)) { - if (a->type == naturalN && b->type == naturalN && a->value%b->value != 0) a->type = rationalN; - a->value = ((a->value << (places6 + 1)) + b->value)/(b->value << 1); - } else { - swprintf(errMsg,L"result of division too large (cannot be %li or larger in magnitude)",shortMax+1); return false; - } - break; - default: - break; - } - return true; // by now -} // ValidBinaryOperation - -bool PrivateControlValueTable::Expression(ActParam *actParam) { - Symbol sign,op; - ActParam actParam2; - - sign = plus; - if (this->scanner.sym == plus || this->scanner.sym == minus) { - sign = this->scanner.sym; - if (!this->scanner.GetSym()) return false; - } - if (!this->Term(actParam)) return false; - if (sign == minus) actParam->value = -actParam->value; - while (this->scanner.sym == plus || this->scanner.sym == minus) { - op = this->scanner.sym; - if (!this->scanner.GetSym()) return false; - if (!this->Term(&actParam2)) return false; - if (!ValidBinaryOperation(actParam,&actParam2,op,this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - } - return true; // by now -} // PrivateControlValueTable::Expression - -bool PrivateControlValueTable::Term(ActParam *actParam) { - Symbol op; - ActParam actParam2; - - if (!this->Factor(actParam)) return false; - while (this->scanner.sym == times || this->scanner.sym == divide) { - op = this->scanner.sym; - if (!this->scanner.GetSym()) return false; - if (!this->Factor(&actParam2)) return false; - if (!ValidBinaryOperation(actParam,&actParam2,op,this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - } - return true; // by now -} // PrivateControlValueTable::Term - -bool PrivateControlValueTable::Factor(ActParam *actParam) { - if (natural <= this->scanner.sym && this->scanner.sym <= rational) { - actParam->type = this->scanner.sym == natural ? naturalN : rationalN; - actParam->value = this->scanner.value; - if (actParam->type == naturalN) actParam->value <<= places6; // standardize for arithmetic - if (!this->scanner.GetSym()) return false; - } else if (this->scanner.sym == leftParen) { - if (!this->scanner.GetSym()) return false; - if (!this->Expression(actParam)) return false; - if (this->scanner.sym != rightParen) { swprintf(this->errMsg,L") expected"); return false; } - if (!this->scanner.GetSym()) return false; - } else { - swprintf(this->errMsg,L"factor starts with invalid symbol (number or ( expected)"); return false; - } - return true; // by now -} // PrivateControlValueTable::Factor - -bool PrivateControlValueTable::PixelAtPpemRange(DeltaColor cmdColor, ActParam *actParam, DeltaColor *paramColor) { - int32_t pixelAmount; - ActParam colorParam; - - if (!this->Parameter(actParam)) return false; - if (!AssertPixelAmount(actParam,-maxPixelValue,maxPixelValue,L"Delta amount",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - pixelAmount = actParam->value; - if (pixelAmount == 0) { swprintf(this->errMsg,L"Delta amount cannot be 0"); this->scanner.ErrUnGetSym(); return false; } - if (!this->Parameter(actParam)) return false; - if (actParam->type < ppemN || multipleRangesOfPpemN < actParam->type) { swprintf(this->errMsg,L"Ppem size(s) expected (Example 10..12;14 for ppem sizes 10 through 12 and ppem size 14)"); this->scanner.ErrUnGetSym(); return false; } - *paramColor = cmdColor; - actParam->deltaColor = alwaysDelta; - if (this->scanner.sym == percent) { // optional delta color sub-parameter - this->scanner.GetSym(); - this->Parameter(&colorParam); - if (colorParam.type != naturalN || DeltaColorOfByte((unsigned char)(colorParam.value/one6)) == invalidDelta) { - swprintf(this->errMsg,L"invalid delta color flag (can be " NARROW_STR_FORMAT L" only)",AllDeltaColorBytes()); - this->scanner.ErrUnGetSym(); return false; - } - actParam->deltaColor = DeltaColorOfByte((unsigned char)(colorParam.value/one6)); - if (cmdColor != alwaysDelta && actParam->deltaColor != alwaysDelta) { - swprintf(this->errMsg,L"cannot override delta color specified by a BDELTA or a GDELTA command"); - this->scanner.ErrUnGetSym(); return false; - } - *paramColor = actParam->deltaColor; - } - actParam->value = pixelAmount; // restore - actParam->type = deltaAtRangeOfPpemN; - return true; // by now -} // PrivateControlValueTable::PixelAtPpemRange - -bool PrivateControlValueTable::PpemRange(ActParam *actParam) { - int32_t i; - - actParam->type = ppemN; // for now - for (i = 0; i < maxPpemSize; i++) actParam->deltaPpemSize[i] = false; - if (!this->Range(actParam)) return false; - while (this->scanner.sym == semiColon) { - if (!this->scanner.GetSym()) return false; - if (!this->Range(actParam)) return false; - actParam->type = multipleRangesOfPpemN; // by now - } - for (i = 0; i < maxPpemSize && !actParam->deltaPpemSize[i]; i++); - actParam->value = i; // assign lowest for single ppem size (such as break ppem size) - return true; // by now -} // PrivateControlValueTable::PpemRange - -bool PrivateControlValueTable::Range(ActParam *actParam) { - ActParam lowParam,highParam; - int32_t low,high,i; - - if (!this->Expression(&lowParam)) return false; - if (!AssertNatural(&lowParam,actParam->lowPpemSize+1,actParam->highPpemSize,L"Ppem size",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - actParam->lowPpemSize = low = high = lowParam.value; - if (this->scanner.sym == ellipsis) { - if (!this->scanner.GetSym()) return false; - if (!this->Expression(&highParam)) return false; - if (!AssertNatural(&highParam,actParam->lowPpemSize+1,actParam->highPpemSize,L"Ppem size",this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } - actParam->lowPpemSize = high = highParam.value; - actParam->type = rangeOfPpemN; // by now - } - for (i = low; i <= Min(maxPpemSize-1,high); i++) actParam->deltaPpemSize[i] = true; - return true; // by now -} // PrivateControlValueTable::Range - -bool PrivateControlValueTable::Compile(TextBuffer*source, TextBuffer*prepText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]) { - int32_t i,firstAvailSubAttributeValue[numSubAttributes]; - uint32_t currAttribute; - Attribute *sortedAttributes; - bool memError = false; - wchar_t comment[maxLineSize],dateTime[32]; - - this->legacyCompile = legacyCompile; - this->tempAttributes = NULL; - this->oldSyntax = this->newSyntax = false; // don't know either way at this point - this->errMsg = errMsg; // copy pointer - this->cvtDataValid = this->cvtDataSorted = false; - DefaultSettings(&this->tempSettings); - this->tempData = NewCvtData(); - this->tt = NewTTSourceEngine(); - if (this->tt) this->tt->InitTTEngine(legacyCompile, &memError); - if (!this->cpgmData || !this->tempData || !this->tt || memError) - { - swprintf(this->errMsg,L"Not enough memory to compile control program"); - goto error; - } - - for (i = firstCharGroup; i <= lastCharGroup; i++) - if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)charGroupToStrg[i],(wchar_t *)charGroupToSpacingText[i],group,i,this->errMsg)) goto error; - firstAvailSubAttributeValue[group] = i; - for (i = firstLinkColor; i <= lastLinkColor; i++) - if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)linkColorToStrg[i],L"",color,i,this->errMsg)) goto error; - firstAvailSubAttributeValue[color] = i; - for (i = firstLinkDirection; i <= lastLinkDirection; i++) - if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)linkDirectionToStrg[i],L"",direction,i,this->errMsg)) goto error; - firstAvailSubAttributeValue[direction] = i; - for (i = firstCvtCategory; i <= lastCvtCategory; i++) - if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)cvtCategoryToStrg[i],L"",category,i,this->errMsg)) goto error; - firstAvailSubAttributeValue[category] = i; - - - this->tt->Emit(L"/* auto-generated pre-program */"); - DateTimeStrg(dateTime); - swprintf(comment,L"/* VTT " WIDE_STR_FORMAT L" compiler " WIDE_STR_FORMAT L" */",VTTVersionString,dateTime); - this->tt->Emit(comment); - this->tt->Emit(L""); - - if (!this->scanner.Init(source,NULL,errMsg)) goto error; - if (!this->InlineSttmt()) goto error; - while (this->scanner.sym == category || this->scanner.sym == group) { - if (!this->AttributeDeclaration(firstAvailSubAttributeValue)) goto error; - if (!this->InlineSttmt()) goto error; - } - - // need to do fpgmBias BEFORE mandatory settings, but mandatory settings before all other settings - if (!this->legacyCompile && this->scanner.sym == fpgmBias) { - if (!this->SettingsDeclaration()) goto error; - if (!this->InlineSttmt()) goto error; - } - - // mandatory settings - if (this->legacyCompile){ - this->tt->SetAspectRatioFlag(); // not needed - }else { - this->tt->SetGreyScalingFlag(); - } - - // compile settings - while (firstSetting <= this->scanner.sym && this->scanner.sym <= lastSetting) { - if (!this->SettingsDeclaration()) goto error; - if (!this->InlineSttmt()) goto error; - } - - // provide defaults for settings if necessary - if (!this->tempSettings.defined[instructionsOn-firstSetting]) { - swprintf(comment,L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[instructionsOn]); this->tt->Emit(comment); - this->tt->INSTCTRL(this->tempSettings.instructionsOnFromPpemSize,this->tempSettings.instructionsOnToPpemSize); - } - if (!this->tempSettings.defined[dropOutCtrlOff-firstSetting] && !this->tempSettings.defined[scanCtrl-firstSetting]) { - swprintf(comment,L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[scanCtrl]); this->tt->Emit(comment); - this->tt->SCANCTRL(this->tempSettings.scanCtrlFlags); - } - if (!this->tempSettings.defined[dropOutCtrlOff-firstSetting] && !this->tempSettings.defined[scanType-firstSetting]) { - swprintf(comment,L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[scanType]); this->tt->Emit(comment); - this->tt->SCANTYPE(this->tempSettings.scanTypeFlags); - } - if (!this->tempSettings.defined[cvtCutIn-firstSetting]) { - swprintf(comment,L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[cvtCutIn]); this->tt->Emit(comment); - this->tt->AssertFreeProjVector(yRomanDir); // so far, this may become aspect-ration dependent, or such like... - this->tt->SCVTCI(this->tempSettings.numCvtCutIns,this->tempSettings.cvtCutInPpemSize,this->tempSettings.cvtCutInValue); - } - - currAttribute = PrivateControlValueTable::PackAttribute(anyGroup,linkAnyColor,linkAnyDir,cvtAnyCategory); - while (this->scanner.sym != eot) { - // if (this->scanner.sym != eol) { - if (!this->CvtDeclaration(&currAttribute)) goto error; - // } - // - // if (this->scanner.sym == eol) { - // if (!this->scanner.GetSym()) goto error; - // } else if (this->scanner.sym != eot) { - // swprintf(errMsg,L"extra text on line"); goto error; - // } - // - } - this->scanner.Term(errPos,errLen); - this->tt->ResetPrepState(); - this->tt->TermTTEngine(this->newSyntax ? prepText : NULL,&memError); - if (memError) - { - swprintf(this->errMsg,L"Not enough memory to compile control program"); - goto error; - } - -#ifdef DEBUGCVT - DebugS(L"LIST OF DEFINED ATTRIBUTES SORTED BY NAME\r"); - DebugS(L"\r"); - Attribute::Dump(this->tempAttributes); - DebugS(L"\r"); -#endif - sortedAttributes = NULL; - if (!Attribute::SortByValue(&sortedAttributes,this->tempAttributes,this->errMsg)) { if (sortedAttributes) delete sortedAttributes; goto error; } - if (this->tempAttributes) delete this->tempAttributes; - this->tempAttributes = sortedAttributes; -#ifdef DEBUGCVT - DebugS(L"LIST OF DEFINED ATTRIBUTES SORTED BY VALUE\r"); - DebugS(L"\r"); - Attribute::Dump(this->tempAttributes); - DebugS(L"\r"); -#endif - - this->cpgmSettings = this->tempSettings; - this->cvtDataValid = true; - for (this->lowestCvtNum = 0; this->lowestCvtNum < maxCvtNum && !(this->tempData[this->lowestCvtNum].flags & cvtDefined); this->lowestCvtNum++); - for (this->highestCvtNum = maxCvtNum-1; this->highestCvtNum >= 0 && !(this->tempData[this->highestCvtNum].flags & cvtDefined); this->highestCvtNum--); - - this->newNumCharGroups = firstAvailSubAttributeValue[group]; - DisposeCvtData(&this->cpgmData); - this->cpgmData = this->tempData; - this->tempData = NULL; - if (this->attributes) delete this->attributes; - this->attributes = this->tempAttributes; - this->tempAttributes = NULL; -#ifdef DEBUGCVT - if (!this->newSyntax) { - DumpOldSyntaxInNewFormat(this); - } -#endif - delete this->tt; this->tt = NULL; - return true; -error: - this->scanner.Term(errPos,errLen); - DisposeCvtData(&this->tempData); - if (this->tempAttributes) { delete this->tempAttributes; this->tempAttributes = NULL; } - if (this->tt) { this->tt->TermTTEngine(NULL,&memError); delete this->tt; this->tt = NULL; } - return false; -} // PrivateControlValueTable::Compile - -bool PrivateControlValueTable::IsControlProgramFormat(void) { - return this->newSyntax; -} // PrivateControlValueTable::IsControlProgramFormat - -bool PrivateControlValueTable::LinearAdvanceWidths(void) { - if (!this->legacyCompile) - { - return this->cpgmSettings.linearAdvanceWidthsFlag > 0; - } - else - { - return false; - } -} // PrivateControlValueTable::LinearAdvanceWidths - - -int32_t PrivateControlValueTable::LowestCvtNum(void) { - return this->cvtDataValid ? this->lowestCvtNum : maxCvtNum; -} // PrivateControlValueTable::LowestCvtNum - -int32_t PrivateControlValueTable::HighestCvtNum(void) { - return this->cvtDataValid ? this->highestCvtNum : -1; -} // PrivateControlValueTable::HighestCvtNum - -int32_t PrivateControlValueTable::LowestCvtIdx(void) { - this->AssertSortedCvt(); - return this->cvtDataSorted ? this->lowestCvtIdx : maxCvtNum; -} // PrivateControlValueTable::LowestCvtIdx - -int32_t PrivateControlValueTable::HighestCvtIdx(void) { - this->AssertSortedCvt(); - return this->cvtDataSorted ? this->highestCvtIdx : -1; -} // PrivateControlValueTable::HighestCvtIdx - -int32_t PrivateControlValueTable::CvtNumOf(int32_t idx) { - this->AssertSortedCvt(); - return this->cvtDataSorted && this->lowestCvtIdx <= idx && idx <= this->highestCvtIdx ? this->cvtKeyOfIdx[idx].num : invalidCvtNum; -} // PrivateControlValueTable::CvtNumOf - -int32_t PrivateControlValueTable::CvtIdxOf(int32_t num) { - this->AssertSortedCvt(); - return this->cvtDataSorted && this->lowestCvtNum <= num && num <= this->highestCvtNum ? this->cvtIdxOfNum[num] : -1; -} // PrivateControlValueTable::CvtIdxOf - -#define Exists(this,cvtNum) (this->cvtDataValid && 0 <= cvtNum && cvtNum <= this->highestCvtNum && this->cpgmData[cvtNum].flags & cvtDefined) -#define AttribExists(this,cvtNum) (Exists(this,cvtNum) && this->cpgmData[cvtNum].flags & attributeDefined) - -bool PrivateControlValueTable::CvtNumExists(int32_t cvtNum) { - return Exists(this,cvtNum); -} // PrivateControlValueTable::CvtNumExists - -bool PrivateControlValueTable::GetCvtValue(int32_t cvtNum, short *cvtValue) { - if (!Exists(this,cvtNum)) return false; - *cvtValue = this->cpgmData[cvtNum].value; - return true; -} // PrivateControlValueTable::GetCvtValue - -bool PrivateControlValueTable::CvtAttributesExist(int32_t cvtNum) { - return AttribExists(this,cvtNum); -} // PrivateControlValueTable::CvtAttributesExist - -bool PrivateControlValueTable::GetCvtAttributes(int32_t cvtNum, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory, bool *relative) { - if (!AttribExists(this,cvtNum)) return false; - PrivateControlValueTable::UnpackAttribute(this->cpgmData[cvtNum].attribute,charGroup,linkColor,linkDirection,cvtCategory); - *relative = (this->cpgmData[cvtNum].flags & relativeValue) != 0; - return true; -} // PrivateControlValueTable::GetCvtAttributes - -int32_t PrivateControlValueTable::NumCharGroups(void) { - return this->cvtDataValid ? this->newNumCharGroups : 0; -} // PrivateControlValueTable::NumCharGroups - -bool PrivateControlValueTable::GetAttributeStrings(int32_t cvtNum, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[], wchar_t relative[]) { - charGroup[0] = linkColor[0] = linkDirection[0] = cvtCategory[0] = relative[0] = L'\0'; - if (!AttribExists(this,cvtNum)) return false; - PrivateControlValueTable::UnpackAttributeStrings(this->cpgmData[cvtNum].attribute,charGroup,linkColor,linkDirection,cvtCategory); - swprintf(relative,WIDE_STR_FORMAT,this->cpgmData[cvtNum].flags & relativeValue ? L"relative" : L"absolute"); - return true; -} // PrivateControlValueTable::GetAttributeStrings - -bool PrivateControlValueTable::GetCharGroupString(CharGroup charGroup, wchar_t string[]) { - wchar_t errMsg[maxLineSize]; - - string[0] = L'\0'; - if (!this->cvtDataValid || charGroup < 0 || this->newNumCharGroups <= charGroup) return false; - return Attribute::SearchByValue(this->attributes,group,charGroup,string,NULL,errMsg); -} // PrivateControlValueTable::GetCharGroupString - -bool PrivateControlValueTable::GetSpacingText(CharGroup charGroup, wchar_t spacingText[]) { - wchar_t errMsg[maxLineSize]; - - spacingText[0] = L'\0'; - if (!this->cvtDataValid || group < 0 || this->newNumCharGroups <= group) return false; - return Attribute::SearchByValue(this->attributes,group,charGroup,NULL,spacingText,errMsg); -} // PrivateControlValueTable::GetSpacingText - -#define CompareKey(a,b) ((a).attribute < (b).attribute ? -1 : ((a).attribute > (b).attribute ? +1 : (int32_t)(a).value - (int32_t)(b).value)) - -int32_t PrivateControlValueTable::GetBestCvtMatch(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory, int32_t distance) { - int32_t attempt,firstAttempt,low,high,mid,cmp,lowCvtNum,highCvtNum; - uint32_t lowAttr,highAttr,lowValue,highValue,catMask4cvtDummy; - CvtKey cvtKey,targetKey; - - if (!this->cvtDataValid || cvtCategory == cvtAnyCategory) return invalidCvtNum; - - if (cvtCategory == -1) { - catMask4cvtDummy = ~subAttributeMask; // since cvtCategory occupies lowest 8 bits... - firstAttempt = 1; - cvtCategory = cvtAnyCategory; - } else { - catMask4cvtDummy = 0xffffffff; - firstAttempt = 0; - } - - this->AssertSortedCvt(); - - targetKey.value = (unsigned short)(distance + 0x8000); - for (attempt = firstAttempt; attempt <= 2; attempt++) { - switch (attempt) { - case 0: break; - case 1: cvtCategory = cvtAnyCategory; break; - case 2: linkDirection = linkAnyDir; break; - } - targetKey.attribute = PrivateControlValueTable::PackAttribute(charGroup,linkColor,linkDirection,cvtCategory) & catMask4cvtDummy; - - low = this->lowestCvtIdx; high = this->highestCvtIdx; - do { // binary search - mid = (low + high) >> 1; - cvtKey = this->cvtKeyOfIdx[mid]; - cvtKey.attribute &= catMask4cvtDummy; - cmp = CompareKey(targetKey,cvtKey); - if (!cmp) return this->cvtKeyOfIdx[mid].num; // found - if (cmp > 0) low = mid + 1; else high = mid - 1; - } while (low <= high); - // since by now low > high => lowValue > highValue - lowCvtNum = this->cvtKeyOfIdx[low].num; - highCvtNum = this->cvtKeyOfIdx[high].num; - lowAttr = this->cvtKeyOfIdx[low].attribute & catMask4cvtDummy; - highAttr = this->cvtKeyOfIdx[high].attribute & catMask4cvtDummy; - lowValue = this->cvtKeyOfIdx[low].value; - highValue = this->cvtKeyOfIdx[high].value; - if (lowAttr == targetKey.attribute && highAttr == targetKey.attribute) return lowValue - targetKey.value < targetKey.value - highValue ? lowCvtNum : highCvtNum; - else if (lowAttr == targetKey.attribute) return lowCvtNum; - else if (highAttr == targetKey.attribute) return highCvtNum; - // else there is no cvt in this linkDirection and cvtCategory - } - return invalidCvtNum; // by now -} // PrivateControlValueTable::GetBestCvtMatch - -void PrivateControlValueTable::PutCvtBinary(int32_t size, unsigned char data[]) { - short *cvtData = (short *)data; - int32_t i; - - this->lowestCvtNum = 0; // we don't know any better at this point, plus there are no valid attributes... - this->highestCvtNum = (size >> 1) - 1; - for (i = 0; i <= this->highestCvtNum; i++, cvtData++) this->cpgmData[i].value = SWAPW(*cvtData); -} // PrivateControlValueTable::PutCvtBinary - -void PrivateControlValueTable::GetCvtBinary(int32_t *size, unsigned char data[]) { - short *cvtData = (short *)data; - int32_t i; - - *size = (this->highestCvtNum + 1) << 1; - for (i = 0; i <= this->highestCvtNum; i++, cvtData++) *cvtData = SWAPW(this->cpgmData[i].value); -} // PrivateControlValueTable::GetCvtBinary - -int32_t PrivateControlValueTable::GetCvtBinarySize(void) { - return (this->highestCvtNum + 1) << 1; -} // PrivateControlValueTable::GetCvtBinarySize - -uint32_t PrivateControlValueTable::PackAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory) { - return ((((((uint32_t)charGroup << subAttributeBits) + (uint32_t)linkColor) << subAttributeBits) + (uint32_t)linkDirection) << subAttributeBits) + (uint32_t)cvtCategory; -} // PrivateControlValueTable::PackAttribute - -void PrivateControlValueTable::UnpackAttribute(uint32_t attribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory) { - *cvtCategory = (CvtCategory) (attribute & subAttributeMask); attribute >>= subAttributeBits; - *linkDirection = (LinkDirection)(attribute & subAttributeMask); attribute >>= subAttributeBits; - *linkColor = (LinkColor) (attribute & subAttributeMask); attribute >>= subAttributeBits; - *charGroup = (CharGroup) (attribute & subAttributeMask); -} // PrivateControlValueTable::UnpackAttribute - -void PrivateControlValueTable::UnpackAttributeStrings(uint32_t attribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]) { - CharGroup theGroup; - LinkColor theColor; - LinkDirection theDirection; - CvtCategory theCategory; - wchar_t errMsg[maxLineSize]; - - PrivateControlValueTable::UnpackAttribute(attribute,&theGroup,&theColor,&theDirection,&theCategory); - if (!Attribute::SearchByValue(this->attributes,group, theGroup, charGroup, NULL,errMsg)) charGroup[0] = L'\0'; - if (!Attribute::SearchByValue(this->attributes,color, theColor, linkColor, NULL,errMsg)) linkColor[0] = L'\0'; - if (!Attribute::SearchByValue(this->attributes,direction,theDirection,linkDirection,NULL,errMsg)) linkDirection[0] = L'\0'; - if (!Attribute::SearchByValue(this->attributes,category, theCategory, cvtCategory, NULL,errMsg)) cvtCategory[0] = L'\0'; -} // PrivateControlValueTable::UnpackAttributeStrings - -void PrivateControlValueTable::AssertSortedCvt(void) { - int32_t cvtNum,cvtIdx,actCvtValue; - ControlValue *cvt; - uint32_t attribute; - CharGroup aGroup; - LinkColor aColor; - LinkDirection aDir; - CvtCategory aCat; - - if (!this->cvtDataValid) return; // nothing to do - - if (this->cvtDataSorted) return; // we're done - - // produce compacted and sorted array of cvtAttribute|cvtValue keys along with cvtNumber data - cvtIdx = 0; - this->cvtKeyOfIdx[cvtIdx].attribute = 0; - this->cvtKeyOfIdx[cvtIdx].value = 0; // low sentinel - this->cvtKeyOfIdx[cvtIdx].num = invalidCvtNum; // cvtNum irrelevant, just silence BC - cvtIdx++; - for (cvtNum = 0; cvtNum <= this->highestCvtNum; cvtNum++) { - cvt = &this->cpgmData[cvtNum]; - if (cvt->flags & cvtDefined) { // skip the gaps - attribute = cvt->attribute; - PrivateControlValueTable::UnpackAttribute(attribute,&aGroup,&aColor,&aDir,&aCat); - if (aCat == cvtRoundHeight) aCat = cvtSquareHeight; // map them all to square until we determine features automatically - actCvtValue = cvt->value; - if (cvt->flags & relativeValue) { // make it an absolute height for the purpose of finding the best match - actCvtValue += this->cpgmData[cvt->parent].value; - attribute = PrivateControlValueTable::PackAttribute(aGroup,aColor,aDir,aCat); - } - this->cvtKeyOfIdx[cvtIdx].attribute = attribute; - this->cvtKeyOfIdx[cvtIdx].value = (unsigned short)(actCvtValue + 0x8000); // bias cvt values - this->cvtKeyOfIdx[cvtIdx].num = (short)cvtNum; - cvtIdx++; - } - } - this->cvtKeyOfIdx[cvtIdx].attribute = 0xffffffff; - this->cvtKeyOfIdx[cvtIdx].value = 0xffff; // high sentinel - this->cvtKeyOfIdx[cvtIdx].num = invalidCvtNum; // cvtNum irrelevant, just silence BC - cvtIdx++; - this->lowestCvtIdx = 1; - this->highestCvtIdx = cvtIdx-2; - this->SortCvtKeys(0,cvtIdx-1); - for (cvtNum = this->lowestCvtNum; cvtNum <= this->highestCvtNum; cvtNum++) this->cvtIdxOfNum[cvtNum] = invalidCvtNum; // init possibly sparsely populated cvt - for (cvtIdx = this->lowestCvtIdx; cvtIdx <= this->highestCvtIdx; cvtIdx++) this->cvtIdxOfNum[this->cvtKeyOfIdx[cvtIdx].num] = (short)cvtIdx; // setup bijectivity - this->cvtDataSorted = true; -} // PrivateControlValueTable::AssertSortedCvt - -void PrivateControlValueTable::SortCvtKeys(int32_t low, int32_t high) { // quicksort - int32_t i,j; - CvtKey midKey,auxKey; - - if (low < high) { - i = low; j = high; - midKey = this->cvtKeyOfIdx[(i + j) >> 1]; - do { - while (CompareKey(this->cvtKeyOfIdx[i],midKey) < 0) i++; - while (CompareKey(midKey,this->cvtKeyOfIdx[j]) < 0) j--; - if (i <= j) { auxKey = this->cvtKeyOfIdx[i]; this->cvtKeyOfIdx[i] = this->cvtKeyOfIdx[j]; this->cvtKeyOfIdx[j] = auxKey; i++; j--; } - } while (i <= j); - this->SortCvtKeys(low,j); - this->SortCvtKeys(i,high); - } -} // PrivateControlValueTable::SortCvtKeys - -/***** -; Auto CharacterGroup.dat for the font UNICOURC.TTF 2.1 Beta2 -; Numbered columns represent: -; Microsoft Unicode -; Mac code -; Apple Unicode -; Dummy code (a count) -; Character type - YOU MUST CORRECT THIS -; and PostScript Name - -* 0 0 0 0 <-Minimum -* FFEE FF FFEE FFEE <-Maximum - -0020 20 0020 4 OTHER space -0021 21 0021 5 OTHER exclam -0022 22 0022 6 OTHER quotedbl -0023 23 0023 7 OTHER numbersign -0024 24 0024 8 FIGURE dollar -0391 ** 0391 384 UPPERCASE Alpha -*****/ - -/***** -#define maxColumns 6 -#define cglookahead 1 -#define maxColumnSize cvtAttributeStrgLen -#define cgBufSize 0x400 -#define eolCh '\r' -*****/ - -#define unknownUnicode 0xffff - -bool PrivateControlValueTable::DumpControlValueTable(TextBuffer *text) { - int32_t cvtNum,pos; - bool newFormat; - wchar_t dump[maxLineSize],groupStrg[32],colorStrg[32],directionStrg[32],categoryStrg[32],relativeStrg[32]; - - newFormat = this->IsControlProgramFormat(); - for (cvtNum = this->LowestCvtNum(); cvtNum <= this->HighestCvtNum(); cvtNum++) { - short cvtValue; - if (this->GetCvtValue(cvtNum,&cvtValue)) { - pos = swprintf(dump,L"%4li: %6i",cvtNum,cvtValue); - if (this->CvtAttributesExist(cvtNum)) { - this->GetAttributeStrings(cvtNum,groupStrg,colorStrg,directionStrg,categoryStrg,relativeStrg); - pos += swprintf(&dump[pos],L" /* " WIDE_STR_FORMAT L" " WIDE_STR_FORMAT L" " WIDE_STR_FORMAT L" " WIDE_STR_FORMAT L" (" WIDE_STR_FORMAT L") */",groupStrg,colorStrg,directionStrg,categoryStrg,relativeStrg); - } - text->AppendLine(dump); - } - } - return true; // by now -} // PrivateControlValueTable::DumpControlValueTable - -bool PrivateControlValueTable::CompileCharGroup(File *from, short platformID, unsigned char toCharGroupOfCharCode[], wchar_t errMsg[]) { - int32_t aGroup,errPos,errLen,row,col,theCol,code[4],platToCol[5] = {2, 1, 2 /* bug??? */, 0, 3}; // plat_Unicode, plat_Macintosh, plat_ISO, plat_MS, unknown case 4 - wchar_t data[2][cvtAttributeStrgLen]; - Scanner scanner; - Attribute *groups; - Symbol subAttribute; - - if (platformID < 0 || 4 < platformID) platformID = plat_MS; - theCol = platToCol[platformID]; - - row = col = 0; - groups = NULL; - for (aGroup = 0; aGroup < this->newNumCharGroups && Attribute::SearchByValue(this->attributes,group,aGroup,data[0],NULL,errMsg) && Attribute::InsertByName(&groups,false,data[0],NULL,group,aGroup,errMsg); aGroup++); - if (aGroup < this->newNumCharGroups) goto error; - if (!scanner.Init(NULL,from,errMsg)) goto error; - while (scanner.sym != eot) { - col = 0; - while (col < 4 && (scanner.sym == (col < 3 ? hexadecimal : natural) || scanner.sym == times)) { - code[col] = scanner.sym == times ? unknownUnicode : scanner.value; - col++; - if (!scanner.GetSym()) goto error; - } - if (col < 4) { swprintf(errMsg,WIDE_STR_FORMAT L" number expected",col < 3 ? L"hexadecimal" : L"decimal"); goto error; } - while (col < 6 && scanner.sym == ident) { - AssignString(data[col-4],scanner.literal,cvtAttributeStrgLen); - col++; - if (!scanner.GetSym()) goto error; - } - if (col < 6) { swprintf(errMsg,WIDE_STR_FORMAT L" expected",col < 5 ? L"character group" : L"postscript name"); goto error; } - if (!Attribute::SearchByName(groups,data[0],NULL,&subAttribute,&aGroup,errMsg) || subAttribute != group) goto error; - if (code[theCol] != unknownUnicode) toCharGroupOfCharCode[code[theCol]] = (unsigned char)aGroup; - row++; - } - scanner.Term(&errPos,&errLen); - if (groups) delete groups; - return true; -error: - swprintf(&errMsg[STRLENW(errMsg)],L" on line %li, column %li",row,col); - scanner.Term(&errPos,&errLen); - if (groups) delete groups; - return false; -} // PrivateControlValueTable::CompileCharGroup - - -ControlValueTable *NewControlValueTable(void) { - return new PrivateControlValueTable; -} // NewControlValueTable - -short PackCvtHexAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory) { - return charGroupToInt[charGroup] << groupPos | linkColorToInt[linkColor] << colorPos | linkDirectionToInt[linkDirection] << dirPos | cvtCategoryToInt[cvtCategory] << catPos; -} // PackCvtHexAttribute - -void UnpackCvtHexAttribute(short hexAttribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory) { - *cvtCategory = intToCvtCategory[ hexAttribute >> catPos & catMask]; - *linkDirection = intToLinkDirection[hexAttribute >> dirPos & dirMask]; - *linkColor = intToLinkColor[ hexAttribute >> colorPos & colorMask]; - *charGroup = intToCharGroup[ hexAttribute >> groupPos & groupMask]; -} // UnpackCvtHexAttribute - -void UnpackCvtHexAttributeStrings(short hexAttribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]) { - CharGroup group; - LinkColor color; - LinkDirection direction; - CvtCategory category; - - UnpackCvtHexAttribute(hexAttribute,&group,&color,&direction,&category); - STRCPYW(charGroup,charGroupToStrg[group]); - STRCPYW(linkColor,linkColorToStrg[color]); - STRCPYW(linkDirection,linkDirectionToStrg[direction]); - STRCPYW(cvtCategory,cvtCategoryToStrg[category]); -} // UnpackCvtHexAttributeStrings +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#define _CRT_SECURE_NO_DEPRECATE + +#include // swprintf +#include // wcscpy, memcpy +#include "pch.h" +#include "List.h" +#include "CvtManager.h" +#include "TTEngine.h" // maxPpemSize & al. + +// #define DEBUGCVT + +/***** from cluster.h, for backwards compatibility with existing "high-level" control value tables (cvt "comments") + + 1 1 1 1 1 1 + 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ++---+-----+---+-----+-----------+ +| ? |Group| C | Dir | Feature | ++---+-----+---+-----+-----------+ + +14 bits used => use 14th bit to indicate a defined cvt entry + +*****/ + +/* group 3 bits */ +#define C_ANYGROUP 0 +#define C_UC 1 +#define C_LC 2 +#define C_FIG 3 +#define C_OTHER 4 +//#define C_RESERVED1 5 +#define C_NONLATIN 5 // @@TM::LIGHT +#define C_RESERVED2 6 +#define C_RESERVED3 7 +#define groupPos (colorPos + colorBits) +#define groupBits 3 +#define groupMask (~((uint32_t)(-1) << groupBits)) + +/* color 2 bits */ +#define C_GREY 0 +#define C_BLACK 1 +#define C_WHITE 2 +#define C_ANYCOLOR 3 +#define colorPos (dirPos + dirBits) +#define colorBits 2 +#define colorMask (~((uint32_t)(-1) << colorBits)) + +/* direction 3 bits */ +#define C_XDIR 1 +#define C_YDIR 2 +#define C_DIAG 3 +#define C_ANYDIR 4 +#define dirPos (catPos + catBits) +#define dirBits 3 +#define dirMask (~((uint32_t)(-1) << dirBits)) + +/* features 6 bits */ +#define C_DUMMY 0 +#define C_STROKE 1 +#define C_ROUND 2 +#define C_DISTANCE 3 +#define C_BEND_RADIUS 4 +#define C_LSB 5 +#define C_RSB 6 +#define C_SERIF_THIN 7 +#define C_SERIF_HEIGHT 8 +#define C_SERIF_EXT 9 +#define C_SERIF_CRVHGT 10 /* for triangular serifs */ +#define C_SERIF_OTHER 11 /* for triangular serifs */ +#define C_BLACKBODYWIDTH 12 +#define C_SERIFBOTTOM 13 +#define C_BRANCH_BEND_RADIUS 14 /* Type 2 bends */ +#define C_SCOOP_DEPTH 15 +#define C_NEW_STRAIGHT 16 +#define C_NEW_DIAGONAL 17 +#define C_NEW_ANYDIR 18 +#define C_SQUARE_HEIGHT 19 // new cvt category for absolute (square) heights +#define C_ROUND_HEIGHT 20 // new cvt category for relative (round, accent) heights +#define C_ITALIC_RUN 21 // new cvt category, too +#define C_ITALIC_RISE 22 // new cvt category, too +#define catPos 0 +#define catBits 6 +#define catMask (~((uint32_t)(-1) << catBits)) + +// @@TM::LIGHT - begin +const short charGroupToInt[numCharGroups] = {C_ANYGROUP, C_OTHER, C_UC, C_LC, C_FIG, C_NONLATIN, C_RESERVED2, C_RESERVED3}; +const wchar_t charGroupToStrg[numCharGroups][cvtAttributeStrgLen] = {L"AnyGroup", L"Other", L"UpperCase", L"LowerCase", L"Figure", L"Reserved1", L"Reserved2", L"Reserved3"}; +const wchar_t charGroupToSpacingText[numCharGroups][cvtAttributeStrgLen] = {L" ", L"HH HOHO OO ", L"HH HOHO OO ", L"nn nono oo ", L"11 1010 00 ", L" ", L" ", L" "}; +const CharGroup intToCharGroup[numCharGroups] = {anyGroup, upperCase, lowerCase, figureCase, otherCase, nonLatinCase, reservedCase2, reservedCase3}; +// @@TM::LIGHT - end + + +const short linkColorToInt[numLinkColors] = {C_ANYCOLOR, C_BLACK, C_GREY, C_WHITE}; +const wchar_t linkColorToStrg[numLinkColors][cvtAttributeStrgLen] = {L"AnyColor", L"Black", L"Grey", L"White"}; +const LinkColor intToLinkColor[numLinkColors] = {linkGrey, linkBlack, linkWhite, linkAnyColor}; + +const short linkDirectionToInt[numLinkDirections] = {C_ANYDIR, C_XDIR, C_YDIR, C_DIAG}; +const wchar_t linkDirectionToStrg[numLinkDirections][cvtAttributeStrgLen] = {L"AnyDirection", L"X", L"Y", L"Diag"}; +const LinkDirection intToLinkDirection[1 + numLinkDirections] = {linkAnyDir, linkX, linkY, linkDiag, linkAnyDir}; + +const short cvtCategoryToInt[numCvtCategories] = {C_DUMMY, C_DISTANCE, C_STROKE, C_ROUND, + C_LSB, C_RSB, C_BLACKBODYWIDTH, C_SERIF_THIN, C_SERIF_HEIGHT, C_SERIF_EXT, C_SERIF_CRVHGT, C_SERIF_OTHER, C_SERIFBOTTOM, + C_SQUARE_HEIGHT,C_ROUND_HEIGHT,C_ITALIC_RUN,C_ITALIC_RISE, + C_BEND_RADIUS, C_BRANCH_BEND_RADIUS, C_SCOOP_DEPTH, C_NEW_STRAIGHT, C_NEW_DIAGONAL, C_NEW_ANYDIR}; +const wchar_t cvtCategoryToStrg[numCvtCategories][cvtAttributeStrgLen] = {L"AnyCategory", L"Distance", L"StraightStroke", L"RoundStroke", + L"LSB", L"RSB", L"BlackBodyWidth", L"SerifThin", L"SerifHeight", L"SerifExt", L"SerifCurveHeight", L"SerifOther", L"SerifBottom", + L"SquareHeight", L"RoundHeight", L"ItalicRun", L"ItalicRise", + L"BendRadius", L"BranchBendRadius", L"ScoopDepth", L"NewStraight", L"NewDiagonal", L"NewAnyFeature"}; +const CvtCategory intToCvtCategory[numCvtCategories] = {cvtAnyCategory, cvtStroke, cvtRound, cvtDistance, + cvtBendRadius, cvtLsb, cvtRsb, cvtSerifThin, cvtSerifHeight, cvtSerifExt, cvtSerifCurveHeight, cvtSerifOther, cvtBlackBody, + cvtSerifBottom, cvtBranchBendRadius, cvtScoopDepth, cvtNewStraightStroke, cvtNewDiagStroke, cvtNewAnyCategory, cvtSquareHeight, cvtRoundHeight, cvtItalicRun, cvtItalicRise}; + +#define DebugS(s) (ParentView::ActiveApplication()->ApplicationError(s)) + +#define lookahead 2 // #chars + +typedef enum {group = 0, color, direction, category, // attribute declaration and settings first (color and direction are only dummies), + fpgmBias, + instructionsOn, dropOutCtrlOff, scanCtrl, scanType, cvtCutIn, + clearTypeCtrl, linearAdvanceWidths, // they are reserved identifiers, + cvtDelta, bCvtDelta, gCvtDelta, // so are these + asM, ident, // user defined identifier next, acts as sentinel in case searched identifier is not reserved + natural, hexadecimal, rational, literal, + period, comma, ellipsis, colon, semiColon, percent, at, + leftParen, plus, minus, times, divide, rightParen, equals, relatesTo, eot} Symbol; // followed by all the other symbols (ordered, don't re-order) + +#define numKeyWords (int32_t)ident +#define maxKeyWordLen 32 +const wchar_t keyWord[numKeyWords][maxKeyWordLen] = {L"GROUP", L"COLOR", L"DIRECTION", L"CATEGORY", + L"FPgmBias", + L"InstructionsOn", L"DropOutCtrlOff", L"ScanCtrl", L"ScanType", L"CvtCutIn", + L"ClearTypeCtrl", L"LinearAdvanceWidths", + L"Delta", L"BDelta", L"GDelta", L"ASM" }; // cf. above + +#define firstSetting fpgmBias +#define lastSetting linearAdvanceWidths +#define numSettings (lastSetting - firstSetting + 1) + +typedef struct Scanner { // make it a static class, neither want to dynamically allocate nor subclass it +public: + Symbol sym; + int32_t value; + wchar_t literal[maxAsmSize]; + bool Init(TextBuffer*source, File *file, wchar_t errMsg[], size_t errMsgLen); + void Term(int32_t *errPos, int32_t *errLen); + bool GetSym(void); + void ReplaceIdent(const wchar_t capIdent[]); + void ErrUnGetSym(void); +private: + void GetCh(void); + bool SkipComment(void); + bool Skip(void); + bool GetNum(void); + bool GetIdent(void); + bool GetLiteral(void); + TextBuffer*source; // for replacing properly capitalized text + File *file; // when compiling character group + int32_t pos,len; // into and of text + wchar_t *text; + wchar_t ch,ch2; // 2-char look-ahead + int32_t prevSymPos,prevSymEnd,symPos; // symPos >= 0 => error + wchar_t *errMsg = nullptr; + size_t errMsgLen = 0; +} Scanner; + +#define numSubAttributes (int32_t)instructionsOn +#define maxSubAttributes 0x100 // e.g. max # character groups +#define subAttributeBits 8 +#define subAttributeMask (~((uint32_t)(-1) << subAttributeBits)) + +class Attribute { +public: + Attribute(void); + virtual ~Attribute(void); + static bool InsertByName(Attribute **tree, bool predefined, const wchar_t name[], const wchar_t spacingText[], Symbol subAttribute, int32_t value, wchar_t errMsg[], size_t errMsgLen); + static bool SearchByName(Attribute *tree, wchar_t name[], wchar_t actualName[], Symbol *subAttribute, int32_t *value, wchar_t errMsg[], size_t errMsgLen); + static bool InsertByValue(Attribute **tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[], size_t errMsgLen); + static bool SearchByValue(Attribute *tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[], size_t errMsgLen); + static bool SortByValue(Attribute **to, Attribute *from, wchar_t errMsg[], size_t errMsgLen); +#ifdef DEBUGCVT + static void Dump(Attribute *tree); +#endif +private: + Attribute *left,*right; // binary tree + wchar_t name[cvtAttributeStrgLen],spacingText[cvtAttributeStrgLen]; + bool predefined; + Symbol subAttribute; // group..category + int32_t value; +}; + +Attribute::Attribute(void) { + this->left = this->right = NULL; + this->predefined = false; + this->name[0] = L'\0'; + this->spacingText[0] = L'\0'; + this->subAttribute = eot; + this->value = -1; +} // Attribute::Attribute + +Attribute::~Attribute(void) { + if (this->left) delete this->left; + if (this->right) delete this->right; +} // Attribute::~Attribute + +void AssignString(wchar_t d[], const wchar_t s[], int32_t n) { + int32_t i; + + for (i = 0; i < n && *s; i++) *d++ = *s++; + if (i < n) *d = L'\0'; +} // AssignString + +int32_t CompareString(wchar_t a[], wchar_t b[], int32_t n) { + int32_t i; + + for (i = 0; i < n && *a && *b && *a == *b; a++, b++, i++); + return i == n ? 0 : *a - *b; +} // CompareString + +#define QCap(ch) (((ch) & 0xffdf)) // we know at this point that we compare alpha-numeric strings, hence it's enough to clear bit 5 + +int32_t CompareCapString(const wchar_t a[], const wchar_t b[], int32_t n) { + int32_t i; + + for (i = 0; i < n && *a && *b && QCap(*a) == QCap(*b); a++, b++, i++); + return i == n ? 0 : QCap(*a) - QCap(*b); +} // CompareCapString + +bool Attribute::InsertByName(Attribute **tree, bool predefined, const wchar_t name[], const wchar_t spacingText[], Symbol subAttribute, int32_t value, wchar_t errMsg[], size_t errMsgLen) { + int32_t cmp; + + if (!(*tree)) { + *tree = new Attribute; + if (!(*tree)) { swprintf(errMsg, errMsgLen, L"Insufficient memory to define attribute \x22" WIDE_STR_FORMAT L"\x22",name); return false; } + (*tree)->predefined = predefined; + AssignString((*tree)->name,name,cvtAttributeStrgLen); + if (spacingText) AssignString((*tree)->spacingText,spacingText,cvtAttributeStrgLen); + (*tree)->subAttribute = subAttribute; + (*tree)->value = value; + return true; + } else { + cmp = CompareCapString(name,(*tree)->name,cvtAttributeStrgLen); + if (!cmp) { swprintf(errMsg,errMsgLen,L"Attribute \x22" WIDE_STR_FORMAT L"\x22 " WIDE_STR_FORMAT L"defined",(*tree)->name,(*tree)->predefined ? L"is pre-" : L"already "); return false; } + return Attribute::InsertByName(cmp < 0 ? &(*tree)->left : &(*tree)->right,predefined,name,spacingText,subAttribute,value,errMsg,errMsgLen); + } +} // Attribute::InsertByName + +bool Attribute::SearchByName(Attribute *tree, wchar_t name[], wchar_t actualName[], Symbol *subAttribute, int32_t *value, wchar_t errMsg[], size_t errMsgLen) { + int32_t cmp; + + while (tree) { + cmp = CompareCapString(name,tree->name,cvtAttributeStrgLen); + if (!cmp) { + if (actualName) AssignString(actualName,tree->name,cvtAttributeStrgLen); + *subAttribute = tree->subAttribute; + *value = tree->value; + return true; // found + } + tree = cmp < 0 ? tree->left : tree->right; + } + swprintf(errMsg, errMsgLen, L"Attribute \x22" WIDE_STR_FORMAT L"\x22 not defined",name); return false; +} // Attribute::SearchByName + +#define PackKey(subAttribute,value) ((int32_t)(subAttribute) << subAttributeBits | (value)) + +bool Attribute::InsertByValue(Attribute **tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[], size_t errMsgLen) { + int32_t key,thisKey; + + if (!(*tree)) { + *tree = new Attribute; + if (!(*tree)) { swprintf(errMsg, errMsgLen,L"Insufficient memory to insert attribute \x22" WIDE_STR_FORMAT L"\x22",name); return false; } + AssignString((*tree)->name,name,cvtAttributeStrgLen); + AssignString((*tree)->spacingText,spacingText,cvtAttributeStrgLen); + (*tree)->subAttribute = subAttribute; + (*tree)->value = value; + return true; + } else { + key = PackKey(subAttribute,value); thisKey = PackKey((*tree)->subAttribute,(*tree)->value); + if (key == thisKey) { swprintf(errMsg, errMsgLen, L"Attribute \x22" WIDE_STR_FORMAT L"\x22 already inserted",name); return false; } // not expected by now, though + return Attribute::InsertByValue(key < thisKey ? &(*tree)->left : &(*tree)->right,subAttribute,value,name,spacingText,errMsg,errMsgLen); + } +} // Attribute::InsertByValue + +bool Attribute::SearchByValue(Attribute *tree, Symbol subAttribute, int32_t value, wchar_t name[], wchar_t spacingText[], wchar_t errMsg[], size_t errMsgLen) { + int32_t key,thisKey; + + while (tree) { + key = PackKey(subAttribute,value); thisKey = PackKey(tree->subAttribute,tree->value); + if (key == thisKey) { + if (name) AssignString(name,tree->name,cvtAttributeStrgLen); + if (spacingText) AssignString(spacingText,tree->spacingText,cvtAttributeStrgLen); + return true; // found + } + tree = key < thisKey ? tree->left : tree->right; + } + swprintf(errMsg, errMsgLen, L"Attribute \x22" WIDE_STR_FORMAT L"\x22 not defined",name); return false; +} // Attribute::SearchByValue + +bool Attribute::SortByValue(Attribute **to, Attribute *from, wchar_t errMsg[], size_t errMsgLen) { +// alternatively, to be a tad more memory efficient, could traverse source tree in prefix, inserting allocated nodes at the end of each visit, +// making sure to have two valid trees at any intermediate stage, for the purpose of standard de-allocation in case of any error. + if (from) { + if (!Attribute::SortByValue(to,from->left,errMsg,errMsgLen)) return false; + if (!Attribute::SortByValue(to,from->right,errMsg,errMsgLen)) return false; + if (!Attribute::InsertByValue(to,from->subAttribute,from->value,from->name,from->spacingText,errMsg,errMsgLen)) return false; + } + return true; // by now +} // Attribute::SortByValue + +#ifdef DEBUGCVT +void Attribute::Dump(Attribute *tree) { + wchar_t out[maxLineSize]; + + if (tree) { + Attribute::Dump(tree->left); + switch (tree->subAttribute) { + case group: swprintf(out,sizeof(out)/sizeof(wchar_t),L"%-10s %-32s %-32s %10li\r",keyWord[tree->subAttribute],tree->name,tree->spacingText,tree->value); break; + case color: + case direction: + case category: + swprintf(out, sizeof(out) / sizeof(wchar_t) ,L"%-10s %-32s %10li\r", keyWord[tree->subAttribute], tree->name, tree->value); + break; + default: + swprintf(out, sizeof(out) / sizeof(wchar_t) ,L"*INVALID* %-32s %10li\r", tree->name, tree->value); + break; + } + DebugS(out); + Attribute::Dump(tree->right); + } +} // Attribute::Dump +#endif + +// flags' values +#define cvtDefined 1 +#define attributeDefined 2 +#define relativeValue 4 + +typedef struct { +// bool defined,attributeDefined; +// bool relative; + short value; + unsigned short flags; + uint32_t attribute; + short breakPpemSize; + short parent; +// LinearListStruct *delta; +} ControlValue; // 16 bytes (?) + +#define cvtReservedFrom 40 +#define cvtReservedTo 64 + +#define maxCvtCutIns 4L + +typedef struct { + short instructionsOnFromPpemSize,instructionsOnToPpemSize; + short dropOutCtrlOffPpemSize; + short scanCtrlFlags,scanTypeFlags; + short clearTypeCtrlFlag; + short linearAdvanceWidthsFlag; + short fpgmBiasNum; + short numCvtCutIns; + short cvtCutInPpemSize[maxCvtCutIns]; + F26Dot6 cvtCutInValue[maxCvtCutIns]; + bool defined[numSettings]; +} Settings; + +void DefaultSettings(Settings *settings) { + int32_t i; + + settings->instructionsOnFromPpemSize = 8; + settings->instructionsOnToPpemSize = 2047; + settings->dropOutCtrlOffPpemSize = 255; // always on + settings->scanCtrlFlags = 0x100 | settings->dropOutCtrlOffPpemSize; + settings->scanTypeFlags = 5; // smart drop out control + settings->clearTypeCtrlFlag = 0; // not optimized for ClearType + settings->linearAdvanceWidthsFlag = 0; // don't allow fractional ppem sizes + settings->fpgmBiasNum = 0; + settings->numCvtCutIns = 3; + settings->cvtCutInPpemSize[0] = 1; + settings->cvtCutInPpemSize[1] = 29; + settings->cvtCutInPpemSize[2] = 128; + settings->cvtCutInValue[0] = 4*one6; + settings->cvtCutInValue[1] = 3*one6/2; + settings->cvtCutInValue[2] = 0; + for (i = 0; i < numSettings; i++) settings->defined[i] = false; // got defaults only +} // DefaultSettings + +typedef enum { voidParam = 0, naturalN, rationalN, ppemN, rangeOfPpemN, multipleRangesOfPpemN, deltaAtRangeOfPpemN, anyS} ParamType; + +typedef struct { + ParamType type; + int32_t value; + wchar_t *literal; // pointer to scanner's literal for memory efficiency, since we don't have more than 1 string parameter per TMT command + int32_t lowPpemSize,highPpemSize; // in-out for ppemSize related params + bool deltaPpemSize[maxPpemSize]; // here we have possibly more than one rangeOfPpemN parameter, but could implement bit vectors... + DeltaColor deltaColor; // alwaysDelta, blackDelta, greyDelta, ..., same for the entire bit vector deltaPpemSize above +} ActParam; + +typedef struct { + uint32_t attribute; + unsigned short value; // cvt values are biased by 0x8000 + short num; +} CvtKey; + +class PrivateControlValueTable : public ControlValueTable { +public: + PrivateControlValueTable(void); + virtual ~PrivateControlValueTable(void); + virtual bool Compile(TextBuffer*source, TextBuffer*prepText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen); + virtual bool IsControlProgramFormat(void); + virtual bool LinearAdvanceWidths(void); + virtual int32_t LowestCvtNum(void); + virtual int32_t HighestCvtNum(void); + virtual int32_t LowestCvtIdx(void); + virtual int32_t HighestCvtIdx(void); + virtual int32_t CvtNumOf(int32_t idx); + virtual int32_t CvtIdxOf(int32_t num); + virtual bool CvtNumExists(int32_t cvtNum); + virtual bool GetCvtValue(int32_t cvtNum, short *cvtValue); + virtual bool CvtAttributesExist(int32_t cvtNum); // entered a cvt "comment"? + virtual bool GetCvtAttributes(int32_t cvtNum, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory, bool *relative); + virtual int32_t NumCharGroups(void); + virtual bool GetAttributeStrings(int32_t cvtNum, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[], wchar_t relative[], size_t commonStrSize); + virtual bool GetCharGroupString(CharGroup group, wchar_t string[]); + virtual bool GetSpacingText(CharGroup group, wchar_t spacingText[]); + virtual int32_t GetBestCvtMatch(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory, int32_t distance); // returns invalidCvtNum if no match + virtual void PutCvtBinary(int32_t size, unsigned char data[]); + virtual void GetCvtBinary(int32_t *size, unsigned char data[]); + virtual int32_t GetCvtBinarySize(void); + virtual uint32_t PackAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory); + virtual void UnpackAttribute(uint32_t attribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory); + virtual void UnpackAttributeStrings(uint32_t attribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]); + virtual bool DumpControlValueTable(TextBuffer *text); + virtual bool CompileCharGroup(File *from, short platformID, unsigned char toCharGroupOfCharCode[], wchar_t errMsg[], size_t errMsgLen); +private: + Scanner scanner; + TTEngine *tt; + Attribute *attributes,*tempAttributes; + bool oldSyntax,newSyntax; + bool legacyCompile; + wchar_t *errMsg; + size_t errMsgLen; // length in wchar_t of errMsg buffer + bool cvtDataValid,cvtDataSorted; + int32_t lowestCvtNum,highestCvtNum; + int32_t lowestCvtIdx,highestCvtIdx; + int32_t newNumCharGroups; + Settings cpgmSettings,tempSettings; + ControlValue *cpgmData,*tempData; + CvtKey cvtKeyOfIdx[1 + maxCvtNum + 1]; // cvt key sorted by cvtAttribute and cvtValue, with sentinels at either end + short cvtIdxOfNum[maxCvtNum]; // inverse table of above, cvtIdx[cvtNum[idx]] = idx + bool AttributeDeclaration(int32_t firstAvailSubAttributeValue[]); + bool SettingsDeclaration(void); + bool CvtDeclaration(uint32_t *attribute); + bool AttributeAssociation(uint32_t *attribute); + bool ValueAssociation(uint32_t attribute, int32_t *cvtNum, ControlValue **cvt); + bool InheritanceRelation(int32_t cvtNum, ControlValue *cvt); + bool DeltaDeclaration(int32_t cvtNum, ControlValue *cvt); + bool InlineSttmt(void); + bool Parameter(ActParam *actParam); + bool Expression(ActParam *actParam); + bool Term(ActParam *actParam); + bool Factor(ActParam *actParam); + bool PixelAtPpemRange(DeltaColor cmdColor, ActParam *actParam, DeltaColor *paramColor); + bool PpemRange(ActParam *actParam); + bool Range(ActParam *actParam); + void AssertSortedCvt(void); + void SortCvtKeys(int32_t low, int32_t high); +}; + +ControlValue *NewCvtData(void) { + int32_t cvtNum; + ControlValue *cvt,*cvtData; + + cvtData = (ControlValue *)NewP(((int32_t)maxCvtNum)*((int32_t)sizeof(ControlValue))); // all these parens appear to be necessary to convice the vc compiler that the operation should be carried out in int32_t... + if (!cvtData) return NULL; + for (cvt = cvtData, cvtNum = 0; cvtNum < maxCvtNum; cvt++, cvtNum++) { + cvt->flags = 0; + cvt->value = 0; + cvt->attribute = 0; + cvt->breakPpemSize = 0; + cvt->parent = invalidCvtNum; + // cvt->delta = NULL; + } + return cvtData; +} // NewCvtData + +void DisposeCvtData(ControlValue **cvtData) { +// int32_t cvtNum; +// ControlValue *cvt; + + if (!(*cvtData)) return; +// for (cvt = *cvtData, cvtNum = 0; cvtNum < maxCvtNum; cvt++, cvtNum++) { +// if (cvt->delta) ListStruct::Delete((ListStruct **)&cvt->delta); +// } + DisposeP((void **)cvtData); +} // DisposeCvtData + +PrivateControlValueTable::PrivateControlValueTable(void) { + this->legacyCompile = false; + this->tt = NULL; + this->cvtDataValid = this->cvtDataSorted = false; + this->lowestCvtNum = maxCvtNum; + this->highestCvtNum = -1; + this->attributes = this->tempAttributes = NULL; + DefaultSettings(&this->cpgmSettings); + this->cpgmData = NewCvtData(); + this->tempData = NULL; + this->errMsg = nullptr; + this->errMsgLen = 0; +} // PrivateControlValueTable::PrivateControlValueTable + +PrivateControlValueTable::~PrivateControlValueTable(void) { + if (this->cpgmData) DisposeCvtData(&this->cpgmData); + if (this->attributes) delete this->attributes; +// CvtDelta::Flush(); +} // PrivateControlValueTable::~PrivateControlValueTable + +#define WhiteSpace(scanner) (L'\0' < scanner->ch && scanner->ch <= L' ' /* && scanner->ch != '\r' */) +#define InitComment(scanner) (scanner->ch == L'/' && scanner->ch2 == L'*') +#define TermComment(scanner) (scanner->ch == L'*' && scanner->ch2 == L'/') +#define Numeric(ch) (L'0' <= (ch) && (ch) <= L'9') +#define Alpha(ch) ((L'A' <= (ch) && (ch) <= L'Z') || (L'a' <= (ch) && (ch) <= L'z')) +#define shortMax 32767L +#define hShortMax 65535L +#define shortMin -32768L + +void Scanner::GetCh(void) { + this->ch = this->ch2; + this->ch2 = this->text && this->pos < this->len ? this->text[this->pos] : L'\0'; +// this->ch2 = Cap(this->ch2); // don't make it case insensitive for now + this->pos++; +} // Scanner::GetCh + +bool Scanner::SkipComment(void) { + int32_t commentPos = this->pos; + + this->GetCh(); this->GetCh(); + while (this->ch && !TermComment(this)) { + if (InitComment(this)) this->SkipComment(); else this->GetCh(); + } + if (this->ch) { // TermComment + this->GetCh(); this->GetCh(); + } else { + this->symPos = commentPos; swprintf(this->errMsg, this->errMsgLen, L"Comment opened but not closed"); return false; + } + return true; +} // Scanner::SkipComment + +bool Scanner::Skip(void) { + while (WhiteSpace(this) || InitComment(this)) { + if (this->ch <= L' ') + this->GetCh(); + else if (!this->SkipComment()) + return false; + } + return true; +} // Scanner::Skip + +bool Scanner::Init(TextBuffer*source, File *file, wchar_t errMsg[], size_t errMsgLen) { + int32_t i; + size_t textLen; + + this->prevSymPos = this->prevSymEnd = this->symPos = -1; + this->source = source; + this->file = file; + this->errMsg = errMsg; // copy pointer + this->errMsgLen = errMsgLen; // copy length + this->pos = 0; + if (this->source) + { + this->len = source->TheLength(); + this->text = (wchar_t *)NewP((this->len + 1) * sizeof(wchar_t)); // '\0'... + if (!this->text) return false; + + source->GetText(&textLen,this->text); + } + else + { + file->ReadUnicode(&this->len, &this->text); + } + this->text[this->len] = L'\0'; + this->ch2 = 0; // silence BC + for (i = 0; i < lookahead; i++) this->GetCh(); + return this->GetSym(); +} // Scanner::Init + +void Scanner::Term(int32_t *errPos, int32_t *errLen) { + *errPos = this->symPos - lookahead; + *errLen = this->pos - this->symPos; + if (this->text) DisposeP((void **)(&this->text)); +} // Scanner::Term + +bool Scanner::GetNum(void) { + int32_t digit,decPlcs,pwrOf10; + + this->value = 0; + if (this->ch == L'0' && Cap(this->ch2) == L'X') { + this->GetCh(); this->GetCh(); this->ch = Cap(this->ch); + while (Numeric(this->ch) || (L'A' <= this->ch && this->ch <= L'F')) { + digit = this->ch <= L'9' ? (int32_t)this->ch - (int32_t)'0' : (int32_t)this->ch - (int32_t)'A' + 10; + if (this->value*16 + digit > hShortMax) { swprintf(this->errMsg, this->errMsgLen, L"Hexadecimal number too large"); return false; } + this->value = this->value*16 + digit; + this->GetCh(); this->ch = Cap(this->ch); + } + this->sym = hexadecimal; + } else { + while (Numeric(this->ch)) { + digit = (int32_t)this->ch - (int32_t)'0'; + if (this->value*10 + digit > shortMax) { swprintf(this->errMsg, this->errMsgLen, L"Number too large"); return false; } + this->value = this->value*10 + digit; + this->GetCh(); + } + this->sym = natural; // so far + if (this->ch == '.' && this->ch2 != '.') { // distinguish from ellipsis + this->GetCh(); + decPlcs = 0; pwrOf10 = 1; + while (Numeric(this->ch)) { + digit = (int32_t)this->ch - (int32_t)'0'; + if (decPlcs*10 * digit > 1000000L) { swprintf(this->errMsg, this->errMsgLen, L"Too many decimal places"); return false; } // 1/64 = 0.015625 + decPlcs = 10*decPlcs + digit; pwrOf10 *= 10L; + this->GetCh(); + } + if (pwrOf10 > 1) { + this->value = this->value*one6 + (decPlcs*one6 + pwrOf10/2)/pwrOf10; + this->sym = rational; // by now + } + } + } + return true; // by now +} // Scanner::GetNum + +bool Scanner::GetIdent(void) { + int32_t i; + + i = 0; + while (Alpha(this->ch) || Numeric(this->ch)) { + if (i >= cvtAttributeStrgLen) + { + swprintf(this->errMsg, this->errMsgLen, L"Identifier too long (cannot have more than %i characters)",(int32_t)cvtAttributeStrgLen); return false; + } + this->literal[i++] = this->ch; + this->GetCh(); + } + if (i < cvtAttributeStrgLen) this->literal[i] = L'\0'; + if (this->source) { + for (i = 0; i < numKeyWords && CompareCapString(this->literal,(wchar_t *)keyWord[i],cvtAttributeStrgLen); i++); // linear search, there are only a few keywords + this->sym = (Symbol)i; + if (this->sym == color || this->sym == direction) this->sym = ident; // don't allow user to define these, so far at least... + if (this->sym < ident && CompareString(this->literal,(wchar_t *)keyWord[this->sym],cvtAttributeStrgLen)) this->ReplaceIdent((wchar_t *)keyWord[this->sym]); + } else { // compiling character group, there are no reserved words... + this->sym = ident; + } + return true; // by now +} // Scanner::GetIdent + +bool Scanner::GetLiteral(void) { + int32_t i; + + this->GetCh(); + i = 0; + while (this->ch && this->ch != L'"') { + if (i >= maxAsmSize-1) { swprintf(this->errMsg, this->errMsgLen, L"String too long (cannot be longer than %li characters)",maxAsmSize-1); return true; } + this->literal[i++] = this->ch; + this->GetCh(); + } + this->literal[i++] = L'\0'; + if (!this->ch) { swprintf(this->errMsg, this->errMsgLen, L"\x22 expected"); return false; } + this->GetCh(); + this->sym = ::literal; + return true; // by now +} // Scanner::GetLiteral + +bool Scanner::GetSym(void) { + this->sym = eot; + this->prevSymEnd = this->pos; + if (!this->Skip()) return false; + this->prevSymPos = this->symPos; this->symPos = this->pos; + if (Numeric(this->ch)) { + return this->GetNum(); + } else if (Alpha(this->ch)) { + return this->GetIdent(); + } else { + switch (this->ch) { + case L'"' : return this->GetLiteral(); + case L'.' : this->GetCh(); if (this->ch == L'.') { this->GetCh(); this->sym = ellipsis; } else this->sym = period; break; + case L',' : this->GetCh(); this->sym = comma; break; + case L':' : this->GetCh(); this->sym = colon; break; + case L';' : this->GetCh(); this->sym = semiColon; break; + case L'%' : this->GetCh(); this->sym = percent; break; + case L'@' : this->GetCh(); this->sym = at; break; + case L'(' : this->GetCh(); this->sym = leftParen; break; + case L'[' : this->GetCh(); this->sym = leftParen; this->ReplaceIdent(L"("); break; + case L')' : this->GetCh(); this->sym = rightParen; break; + case L']' : this->GetCh(); this->sym = rightParen; this->ReplaceIdent(L")"); break; + case L'+' : this->GetCh(); this->sym = plus; break; + case L'-' : this->GetCh(); this->sym = minus; break; + case L'*' : this->GetCh(); this->sym = times; break; + case L'/' : this->GetCh(); this->sym = divide; break; + case L'=' : this->GetCh(); this->sym = equals; break; + case L'~' : this->GetCh(); this->sym = relatesTo; break; + // case L'\r': this->GetCh(); this->sym = eol; break; + case L'\0': this->sym = eot; break; + default : this->GetCh(); swprintf(this->errMsg, this->errMsgLen, L"Invalid character in control value table"); return false; break; + } + } + return true; +} // Scanner::GetSym + +void Scanner::ReplaceIdent(const wchar_t capIdent[]) { + int32_t beg,end; + + beg = this->symPos - lookahead; + end = beg + (int32_t)STRLENW(capIdent); + this->source->Delete(beg,end); + this->source->Insert(beg,capIdent); +} // Scanner::ReplaceIdent + +void Scanner::ErrUnGetSym(void) { + this->pos = this->prevSymEnd; this->symPos = this->prevSymPos; // after semantical/contextual error, we're one symbol ahead, hence retract for correct error high-lighting +} // Scanner::ErrUnGetSym + +bool AssertNatural(ActParam *actParam, int32_t low, int32_t high, const wchar_t name[], wchar_t errMsg[], size_t errMsgLen) { + if (actParam->type != naturalN) { swprintf(errMsg,errMsgLen,WIDE_STR_FORMAT L" expected (must be an integer in range %li through %li)",name,low,high); return false; } + actParam->value >>= places6; + if (actParam->value < low || high < actParam->value) { swprintf(errMsg,errMsgLen, WIDE_STR_FORMAT L" out of range (must be in range %li through %li)",name,low,high); return false; } + return true; // by now +} // AssertNatural + +bool AssertPixelAmount(ActParam *actParam, F26Dot6 low, F26Dot6 high, const wchar_t name[], wchar_t errMsg[], size_t errMsgLen) { + if (actParam->type == naturalN) actParam->type = rationalN; + if (actParam->type != rationalN) { swprintf(errMsg,errMsgLen,WIDE_STR_FORMAT L" expected (must be a pixel amount in range %8.6f through %8.6f)",name,(double)low/one6,(double)high/one6); return false; } + if (actParam->value < low || high < actParam->value) { swprintf(errMsg,errMsgLen,WIDE_STR_FORMAT L" expected (must be in range %8.6f through %8.6f)",name,(double)low/one6,(double)high/one6); return false; } + return true; // by now +} // AssertPixelAmount + +bool PrivateControlValueTable::AttributeDeclaration(int32_t firstAvailSubAttributeValue[]) { + Symbol sym; + wchar_t name[cvtAttributeStrgLen],spacingText[cvtAttributeStrgLen]; + + this->newSyntax = true; + sym = this->scanner.sym; + if (!this->scanner.GetSym()) return false; + if (this->scanner.sym != ident) { swprintf(this->errMsg, this->errMsgLen, WIDE_STR_FORMAT L" name expected",keyWord[sym]); return false; } + if (firstAvailSubAttributeValue[sym] >= maxSubAttributes) { swprintf(this->errMsg, this->errMsgLen, WIDE_STR_FORMAT L" name exceeds capacity (cannot have more than %li)",keyWord[sym],maxSubAttributes); return false; } + AssignString(name,this->scanner.literal,cvtAttributeStrgLen); + if (!this->scanner.GetSym()) return false; + spacingText[0] = L'\0'; + if (sym == group && this->scanner.sym == literal) { + AssignString(spacingText,this->scanner.literal,cvtAttributeStrgLen); + if (!this->scanner.GetSym()) return false; + } + if (!Attribute::InsertByName(&this->tempAttributes,false,name,spacingText,sym,firstAvailSubAttributeValue[sym],this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + firstAvailSubAttributeValue[sym]++; + return true; // by now + +} // PrivateControlValueTable::AttributeDeclaration + +bool PrivateControlValueTable::SettingsDeclaration(void) { + Symbol sym; + ActParam instrOnParam,dropOffParam,scanCtrlParam,scanTypeParam,cvtCutInPixelSizeParam,cvtCutInPpemSizeParam,clearTypeCtrlParam,linearAdvanceWidthsParam,fpgmBiasParam; + wchar_t comment[maxLineSize]; + + this->newSyntax = true; + sym = this->scanner.sym; + if (this->tempSettings.defined[sym-firstSetting]) { swprintf(this->errMsg, this->errMsgLen, WIDE_STR_FORMAT L" already defined",keyWord[sym]); return false; } + if (!this->scanner.GetSym()) return false; + + if (this->legacyCompile || sym != fpgmBias) { + swprintf(comment, sizeof(comment)/sizeof(wchar_t),L"/* " WIDE_STR_FORMAT L" */",keyWord[sym]); this->tt->Emit(comment); + } + + switch (sym) { + case instructionsOn: + instrOnParam.lowPpemSize = 0; instrOnParam.highPpemSize = shortMax; + if (!this->Parameter(&instrOnParam)) return false; + if (instrOnParam.type != rangeOfPpemN) { swprintf(this->errMsg, this->errMsgLen, L"Range of ppem sizes at which instructions are on expected (Example: @8..2047 to activate instructions in range 8 through 2047 ppem)"); this->scanner.ErrUnGetSym(); return false; } + this->tempSettings.instructionsOnFromPpemSize = (short)instrOnParam.value; + this->tempSettings.instructionsOnToPpemSize = (short)instrOnParam.lowPpemSize; + this->tt->INSTCTRL(this->tempSettings.instructionsOnFromPpemSize,this->tempSettings.instructionsOnToPpemSize); + this->tempSettings.defined[sym - firstSetting] = true; + break; + case dropOutCtrlOff: + if (this->tempSettings.defined[scanCtrl-firstSetting] || this->tempSettings.defined[scanType-firstSetting]) { swprintf(this->errMsg, this->errMsgLen, L"Cannot use " WIDE_STR_FORMAT L" together with " WIDE_STR_FORMAT L" or " WIDE_STR_FORMAT,keyWord[sym],keyWord[scanCtrl],keyWord[scanType]); this->scanner.ErrUnGetSym(); return false; } + dropOffParam.lowPpemSize = -1; dropOffParam.highPpemSize = maxPpemSize-1; // lowest permissible ppem size - 1 + if (!this->Parameter(&dropOffParam)) return false; + if (dropOffParam.type != ppemN) { swprintf(this->errMsg,this->errMsgLen, L"Drop-out control turn-off ppem size expected (must be an integer in range @%li through @%li)" BRK L"Drop-out control turn-off ppem size specifies the ppem size at and above which drop-out control is no longer turned on.",1,dropOffParam.highPpemSize); this->scanner.ErrUnGetSym(); return false; } + this->tempSettings.dropOutCtrlOffPpemSize = (short)dropOffParam.value; + this->tempSettings.scanCtrlFlags = (this->tempSettings.scanCtrlFlags & 0xff00) | this->tempSettings.dropOutCtrlOffPpemSize; + this->tt->SCANCTRL(this->tempSettings.scanCtrlFlags); + this->tt->SCANTYPE(this->tempSettings.scanTypeFlags); + this->tempSettings.defined[sym - firstSetting] = true; + break; + case scanCtrl: + if (this->tempSettings.defined[dropOutCtrlOff-firstSetting]) { swprintf(this->errMsg, this->errMsgLen, L"Cannot use " WIDE_STR_FORMAT L" together with " WIDE_STR_FORMAT,keyWord[sym],keyWord[dropOutCtrlOff]); this->scanner.ErrUnGetSym(); return false; } + if (this->scanner.sym != equals) { swprintf(this->errMsg, this->errMsgLen, L"= expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&scanCtrlParam)) return false; + if (!AssertNatural(&scanCtrlParam,0,16383,L"Value for scan control",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } // bits 14 and 15 reserved for future use + this->tempSettings.scanCtrlFlags = (short)scanCtrlParam.value; + this->tt->SCANCTRL(this->tempSettings.scanCtrlFlags); + this->tempSettings.defined[sym - firstSetting] = true; + break; + case scanType: + if (this->tempSettings.defined[dropOutCtrlOff-firstSetting]) { swprintf(this->errMsg,this->errMsgLen, L"Cannot use " WIDE_STR_FORMAT L" together with " WIDE_STR_FORMAT,keyWord[sym],keyWord[dropOutCtrlOff]); return false; } + if (this->scanner.sym != equals) { swprintf(this->errMsg, this->errMsgLen, L"= expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&scanTypeParam)) return false; + if (!AssertNatural(&scanTypeParam,1,6,L"Value for scan type",this->errMsg,this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + this->tempSettings.scanTypeFlags = (short)scanTypeParam.value; + this->tt->SCANTYPE(this->tempSettings.scanTypeFlags); + this->tempSettings.defined[sym - firstSetting] = true; + break; + case cvtCutIn: + if (this->scanner.sym != equals) { swprintf(this->errMsg, this->errMsgLen, L"= expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&cvtCutInPixelSizeParam)) return false; + if (!AssertPixelAmount(&cvtCutInPixelSizeParam,0,maxPixelValue,L"Cut-in pixel amount",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + this->tempSettings.cvtCutInValue[0] = cvtCutInPixelSizeParam.value; + cvtCutInPpemSizeParam.lowPpemSize = 0; cvtCutInPpemSizeParam.highPpemSize = maxPpemSize-1; + this->tempSettings.numCvtCutIns = 1; + while (this->scanner.sym == comma) { + if (this->tempSettings.numCvtCutIns >= maxCvtCutIns) { swprintf(this->errMsg, this->errMsgLen, L"Too many cvt cut-ins (cannot have more than %li)",maxCvtCutIns); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&cvtCutInPixelSizeParam)) return false; + if (!AssertPixelAmount(&cvtCutInPixelSizeParam,0,this->tempSettings.cvtCutInValue[this->tempSettings.numCvtCutIns-1]-1,L"Cut-in pixel amount",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } // allow at most 1/64 less than preceding cut in + if (!this->Parameter(&cvtCutInPpemSizeParam)) return false; + this->tempSettings.cvtCutInValue[this->tempSettings.numCvtCutIns] = cvtCutInPixelSizeParam.value; + this->tempSettings.cvtCutInPpemSize[this->tempSettings.numCvtCutIns] = (short)cvtCutInPpemSizeParam.value; + this->tempSettings.numCvtCutIns++; + } + this->tt->AssertFreeProjVector(yRomanDir); // so far, this may become aspect-ratio dependent, or such like... + this->tt->SCVTCI(this->tempSettings.numCvtCutIns,this->tempSettings.cvtCutInPpemSize,this->tempSettings.cvtCutInValue); + this->tempSettings.defined[sym - firstSetting] = true; + break; + default: + break; + } + + if (!this->legacyCompile) + { + switch (sym) + { + case clearTypeCtrl: + if (this->scanner.sym != equals) { swprintf(this->errMsg, this->errMsgLen, L"= expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&clearTypeCtrlParam)) return false; + if (!AssertNatural(&clearTypeCtrlParam, 0, 1, L"Value for ClearTypeCtrl", this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + this->tempSettings.clearTypeCtrlFlag = (short) clearTypeCtrlParam.value; + this->tt->SetClearTypeCtrl(this->tempSettings.clearTypeCtrlFlag); + this->tempSettings.defined[sym - firstSetting] = true; + break; + case linearAdvanceWidths: + if (this->scanner.sym != equals) { swprintf(this->errMsg, this->errMsgLen, L"= expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&linearAdvanceWidthsParam)) return false; + if (!AssertNatural(&linearAdvanceWidthsParam, 0, 1, L"Value for linearAdvanceWidths", this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + this->tempSettings.linearAdvanceWidthsFlag = (short) linearAdvanceWidthsParam.value; + // client to inquire flag after CVT compilation and to call font->UpdateAdvanceWidthFlag + this->tempSettings.defined[sym - firstSetting] = true; + break; + case fpgmBias: + if (this->scanner.sym != equals) { swprintf(this->errMsg, this->errMsgLen, L"= expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&fpgmBiasParam)) return false; + if (!AssertNatural(&fpgmBiasParam, 0, 32767, L"Value for FPgmBias", this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + this->tempSettings.fpgmBiasNum = (short) fpgmBiasParam.value; + this->tt->SetFunctionNumberBias(this->tempSettings.fpgmBiasNum); + this->tempSettings.defined[sym - firstSetting] = true; + break; + default: + break; + } + } + + return true; // by now +} // PrivateControlValueTable::SettingsDeclaration + +bool PrivateControlValueTable::CvtDeclaration(uint32_t *attribute) { + int32_t cvtNum; + ControlValue *cvt = nullptr; + + if (!(this->AttributeAssociation(attribute) && // this->InlineSttmt() && + this->ValueAssociation(*attribute,&cvtNum,&cvt) && this->InlineSttmt() && + this->InheritanceRelation(cvtNum,cvt) && this->InlineSttmt() && + this->DeltaDeclaration(cvtNum,cvt) && this->InlineSttmt())) return false; + cvt->flags |= cvtDefined; // at the very end only, or else we would allow referencing self as a parent... + return true; // by now +} // PrivateControlValueTable::CvtDeclaration + +bool PrivateControlValueTable::AttributeAssociation(uint32_t *attribute) { + CharGroup currGroup; + LinkColor currColor; + LinkDirection currDirection; + CvtCategory currCategory; + Symbol subAttribute; + int32_t value; + wchar_t actualName[cvtAttributeStrgLen]; + + PrivateControlValueTable::UnpackAttribute(*attribute,&currGroup,&currColor,&currDirection,&currCategory); + while (this->scanner.sym == ident) { + this->newSyntax = true; + if (!Attribute::SearchByName(this->tempAttributes,this->scanner.literal,actualName,&subAttribute,&value,this->errMsg, this->errMsgLen)) return false; + if (CompareString(this->scanner.literal,actualName,cvtAttributeStrgLen)) this->scanner.ReplaceIdent(actualName); + switch (subAttribute) { + case group: currGroup = (CharGroup)value; break; + case color: currColor = (LinkColor)value; break; + case direction: currDirection = (LinkDirection)value; break; + case category: currCategory = (CvtCategory)value; break; + default: break; + } + if (!this->scanner.GetSym()) return false; + if (!this->InlineSttmt()) return false; + } + *attribute = PrivateControlValueTable::PackAttribute(currGroup,currColor,currDirection,currCategory); + return true; // by now +} // PrivateControlValueTable::AttributeAssociation + +bool PrivateControlValueTable::ValueAssociation(uint32_t attribute, int32_t *cvtNum, ControlValue **cvt) { + ActParam cvtNumParam,cvtValueParam; + CharGroup charGroup; + LinkColor linkColor; + LinkDirection linkDirection; + CvtCategory cvtCategory; + + if (!this->Parameter(&cvtNumParam)) return false; + if (!AssertNatural(&cvtNumParam,0,maxCvtNum-1,L"Cvt number",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + *cvtNum = cvtNumParam.value; +// we're currently not testing this as there may be users with different fpgms and we should really un-hardwire the remaining hard-wired cvts +// if (cvtReservedFrom <= *cvtNum && *cvtNum <= cvtReservedTo) { swprintf(this->errMsg,L"Cvt numbers in range %i through %i are reserved",(int32_t)cvtReservedFrom,(int32_t)cvtReservedTo); this->scanner.ErrUnGetSym(); return false; } + *cvt = &this->tempData[cvtNumParam.value]; + if ((*cvt)->flags & cvtDefined) { swprintf(this->errMsg, this->errMsgLen, L"cvt number already defined"); this->scanner.ErrUnGetSym(); return false; } + if (this->scanner.sym != colon) { swprintf(this->errMsg, this->errMsgLen, L"':' expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&cvtValueParam)) return false; + if (cvtValueParam.type != naturalN) { swprintf(this->errMsg, this->errMsgLen, L"Cvt value expected (must be an integer specifying font design units)"); this->scanner.ErrUnGetSym(); return false; } + cvtValueParam.value >>= places6; + (*cvt)->value = (short)cvtValueParam.value; + if (this->scanner.sym == hexadecimal) { + if (this->newSyntax) { swprintf(this->errMsg, this->errMsgLen, L"Cannot mix cvt formats (hexadecimal attributes are used in the old cvt format only)"); return false; } + this->oldSyntax = true; + UnpackCvtHexAttribute((short)this->scanner.value,&charGroup,&linkColor,&linkDirection,&cvtCategory); + (*cvt)->attribute = PrivateControlValueTable::PackAttribute(charGroup,linkColor,linkDirection,cvtCategory); + (*cvt)->flags |= attributeDefined; + if (!this->scanner.GetSym()) return false; + } else if (this->newSyntax) { + (*cvt)->attribute = attribute; + (*cvt)->flags |= attributeDefined; + } +// (*cvt)->flags |= PostSizeToViewTitle; not yet + return true; // by now +} // PrivateControlValueTable::ValueAssociation + +bool PrivateControlValueTable::InheritanceRelation(int32_t cvtNum, ControlValue *cvt) { + bool relative; + ActParam parentCvtNumParam,ppemValueParam; + ControlValue *parentCvt; + CharGroup charGroup; + LinkColor linkColor; + LinkDirection linkDirection; + CvtCategory cvtCategory; + + if (this->scanner.sym == equals || this->scanner.sym == relatesTo) { + relative = this->scanner.sym == relatesTo; + this->newSyntax = true; + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&parentCvtNumParam)) return false; + if (!AssertNatural(&parentCvtNumParam,0,maxCvtNum-1,L"Parent cvt number",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + parentCvt = &this->tempData[parentCvtNumParam.value]; + if (!(parentCvt->flags & cvtDefined)) { swprintf(this->errMsg, this->errMsgLen, L"Parent cvt not defined (must be completely defined prior to tying child cvts to it)"); this->scanner.ErrUnGetSym(); return false; } +// if (this->scanner.sym != at) { swprintf(this->errMsg,L"'@' expected"); return false; } +// if (!this->scanner.GetSym()) return false; + if (!this->legacyCompile) { + ppemValueParam.lowPpemSize = 0; // lowest permissible ppem size - 1 + } else { + ppemValueParam.lowPpemSize = parentCvt->breakPpemSize; // lowest permissible ppem size - 1 + } + + ppemValueParam.highPpemSize = maxPpemSize-1; + if (!this->Parameter(&ppemValueParam)) return false; + if (ppemValueParam.type != ppemN) { swprintf(this->errMsg, this->errMsgLen, L"Break ppem size expected (must be an integer in range @%li through @%li)" BRK L"The break ppem size specifies the ppem size at which this child cvt is no longer tied to its parent.",parentCvt->breakPpemSize+1,ppemValueParam.highPpemSize); this->scanner.ErrUnGetSym(); return false; } + if (this->legacyCompile){ + if (ppemValueParam.value <= parentCvt->breakPpemSize || ppemValueParam.highPpemSize < ppemValueParam.value) { swprintf(this->errMsg, this->errMsgLen, L"Break ppem size out of range (must be in range @%li through @%li)" BRK L"The break ppem size must be above the break ppem size of the parent of this child cvt)", parentCvt->breakPpemSize + 1, ppemValueParam.highPpemSize); this->scanner.ErrUnGetSym(); return false; } + } + + cvt->parent = (short)parentCvtNumParam.value; + cvt->breakPpemSize = (short)ppemValueParam.value; + if (relative) cvt->flags |= relativeValue; + this->UnpackAttribute(cvt->attribute,&charGroup,&linkColor,&linkDirection,&cvtCategory); + this->tt->AssertFreeProjVector(linkDirection == linkX ? xRomanDir : yRomanDir); + this->tt->CvtRegularization(relative,(short)cvtNum,cvt->breakPpemSize,cvt->parent); + cvt->attribute = this->PackAttribute(charGroup,linkColor,linkDirection,cvtCategory); + } + return true; // by now +} // PrivateControlValueTable::InheritanceRelation + +bool PrivateControlValueTable::DeltaDeclaration(int32_t cvtNum, ControlValue *cvt) { + ActParam pixelAtPpemRangeParam; + bool colorDeltaDone[numDeltaColors]; + DeltaColor cmdColor,paramColor; + int32_t i; + CharGroup charGroup; + LinkColor linkColor; + LinkDirection linkDirection; + CvtCategory cvtCategory; + + for (i = 0; i < numDeltaColors; i++) colorDeltaDone[i] = false; + while (cvtDelta <= this->scanner.sym && this->scanner.sym <= gCvtDelta) { + this->newSyntax = true; + cmdColor = (DeltaColor)(this->scanner.sym-cvtDelta); + if (colorDeltaDone[cmdColor]) { swprintf(this->errMsg, this->errMsgLen, L"Cannot have more than one " WIDE_STR_FORMAT L" command per control value" BRK \ + L"Please combine them to a single " WIDE_STR_FORMAT L" command. Example: " WIDE_STR_FORMAT L"(1 @18..20;22, -1 @ 24..25)", + keyWord[this->scanner.sym],keyWord[this->scanner.sym],keyWord[this->scanner.sym]); return false; } + colorDeltaDone[cmdColor] = true; + if (!this->scanner.GetSym()) return false; + if (this->scanner.sym != leftParen) { swprintf(this->errMsg, this->errMsgLen, L"( expected"); return false; } + if (!this->scanner.GetSym()) return false; + pixelAtPpemRangeParam.lowPpemSize = cvt->breakPpemSize-1; // lowest permissible ppem size - 1 + pixelAtPpemRangeParam.highPpemSize = maxPpemSize-1; + if (!this->PixelAtPpemRange(cmdColor,&pixelAtPpemRangeParam,¶mColor)) return false; +// if (!AppendDeltas(cmdColor,&pixelAtPpemRangeParam,cvt,this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } + this->UnpackAttribute(cvt->attribute,&charGroup,&linkColor,&linkDirection,&cvtCategory); + this->tt->AssertFreeProjVector(linkDirection == linkX ? xRomanDir : yRomanDir); + this->tt->DLT(true,paramColor,(short)cvtNum,pixelAtPpemRangeParam.value,pixelAtPpemRangeParam.deltaPpemSize); + while (this->scanner.sym == comma) { + if (!this->scanner.GetSym()) return false; + for (i = maxPpemSize-1; i > 0 && !pixelAtPpemRangeParam.deltaPpemSize[i]; i--); + pixelAtPpemRangeParam.lowPpemSize = i; // lowest permissible ppem size - 1 + if (!this->PixelAtPpemRange(cmdColor,&pixelAtPpemRangeParam,¶mColor)) return false; +// if (!AppendDeltas(cmdColor,&pixelAtPpemRangeParam,cvt,this->errMsg)) { this->scanner.ErrUnGetSym(); return false; } + this->tt->DLT(true,paramColor,(short)cvtNum,pixelAtPpemRangeParam.value,pixelAtPpemRangeParam.deltaPpemSize); + } + if (this->scanner.sym != rightParen) { swprintf(this->errMsg, this->errMsgLen, L") expected"); return false; } + if (!this->scanner.GetSym()) return false; + } + return true; // by now +} // PrivateControlValueTable::DeltaDeclaration + +bool PrivateControlValueTable::InlineSttmt(void) { + ActParam asmCodeParam; + + while (this->scanner.sym == asM) { + if (!this->scanner.GetSym()) return false; + if (this->scanner.sym != leftParen) { swprintf(this->errMsg, this->errMsgLen, L"( expected"); return false; } + if (!this->scanner.GetSym()) return false; + if (!this->Parameter(&asmCodeParam)) return false; + if (asmCodeParam.type != anyS) { swprintf(this->errMsg, this->errMsgLen, L"Actual TrueType code expected (Example: \x22/* Comment */\x22)"); this->scanner.ErrUnGetSym(); return false; } + this->tt->Emit(this->scanner.literal); + if (this->scanner.sym != rightParen) { swprintf(this->errMsg, this->errMsgLen, L") expected"); return false; } + if (!this->scanner.GetSym()) return false; + } + return true; // by now +} // PrivateControlValueTable::InlineSttmt + +bool PrivateControlValueTable::Parameter(ActParam *actParam) { + if ((natural <= this->scanner.sym && this->scanner.sym <= rational) || (leftParen <= this->scanner.sym && this->scanner.sym <= minus)) { + if (!this->Expression(actParam)) return false; + } else if (this->scanner.sym == at) { + if (!this->scanner.GetSym()) return false; + if (!this->PpemRange(actParam)) return false; + } else if (this->scanner.sym == literal) { + actParam->type = anyS; actParam->literal = this->scanner.literal; + if (!this->scanner.GetSym()) return false; + } else { + swprintf(this->errMsg, this->errMsgLen, L"parameter starts with invalid symbol (+, -, @, number, or \x22string\x22 expected)"); return false; + } + return true; // by now +} // PrivateControlValueTable::Parameter + +bool ValidBinaryOperation(ActParam *a, ActParam *b, Symbol op, wchar_t errMsg[], size_t errMsgLen) { + wchar_t opName[4][10] = {L"add",L"subtract",L"multiply",L"divide"}; + + if (a->type < naturalN || rationalN < a->type || b->type < naturalN || rationalN < b->type) { + swprintf(errMsg, errMsgLen, L"cannot " WIDE_STR_FORMAT L" these operands",opName[op-plus]); return false; + } + a->type = Max(a->type,b->type); + if (op == divide && a->type == naturalN && b->type == naturalN && b->value != 0 && a->value % b->value != 0) a->type = rationalN; + switch (op) { + case plus: + case minus: + if (op == plus) a->value += b->value; else a->value -= b->value; + if (a->value < shortMin << places6) { + swprintf(errMsg, errMsgLen, L"result of subtraction too small (cannot be below %li)",shortMin); return false; + } else if (shortMax << places6 < a->value) { + swprintf(errMsg, errMsgLen, L"result of addition too large (cannot be above %li)",shortMax); return false; + } + break; + case times: + if ((double)Abs(a->value)*(double)Abs(b->value) < (double)((shortMax + 1) << (places6 + places6))) { + a->value = (a->value*b->value + half6) >> places6; + } else { + swprintf(errMsg, errMsgLen, L"result of multiplication too large (cannot be %li or larger in magnitude)",shortMax+1); return false; + } + break; + case divide: + if (b->value != 0 && (double)Abs(a->value) < (double)(shortMax+1)*(double)Abs(b->value)) { + if (a->type == naturalN && b->type == naturalN && a->value%b->value != 0) a->type = rationalN; + a->value = ((a->value << (places6 + 1)) + b->value)/(b->value << 1); + } else { + swprintf(errMsg, errMsgLen, L"result of division too large (cannot be %li or larger in magnitude)",shortMax+1); return false; + } + break; + default: + break; + } + return true; // by now +} // ValidBinaryOperation + +bool PrivateControlValueTable::Expression(ActParam *actParam) { + Symbol sign,op; + ActParam actParam2; + + sign = plus; + if (this->scanner.sym == plus || this->scanner.sym == minus) { + sign = this->scanner.sym; + if (!this->scanner.GetSym()) return false; + } + if (!this->Term(actParam)) return false; + if (sign == minus) actParam->value = -actParam->value; + while (this->scanner.sym == plus || this->scanner.sym == minus) { + op = this->scanner.sym; + if (!this->scanner.GetSym()) return false; + if (!this->Term(&actParam2)) return false; + if (!ValidBinaryOperation(actParam,&actParam2,op,this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + } + return true; // by now +} // PrivateControlValueTable::Expression + +bool PrivateControlValueTable::Term(ActParam *actParam) { + Symbol op; + ActParam actParam2; + + if (!this->Factor(actParam)) return false; + while (this->scanner.sym == times || this->scanner.sym == divide) { + op = this->scanner.sym; + if (!this->scanner.GetSym()) return false; + if (!this->Factor(&actParam2)) return false; + if (!ValidBinaryOperation(actParam,&actParam2,op,this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + } + return true; // by now +} // PrivateControlValueTable::Term + +bool PrivateControlValueTable::Factor(ActParam *actParam) { + if (natural <= this->scanner.sym && this->scanner.sym <= rational) { + actParam->type = this->scanner.sym == natural ? naturalN : rationalN; + actParam->value = this->scanner.value; + if (actParam->type == naturalN) actParam->value <<= places6; // standardize for arithmetic + if (!this->scanner.GetSym()) return false; + } else if (this->scanner.sym == leftParen) { + if (!this->scanner.GetSym()) return false; + if (!this->Expression(actParam)) return false; + if (this->scanner.sym != rightParen) { swprintf(this->errMsg, this->errMsgLen, L") expected"); return false; } + if (!this->scanner.GetSym()) return false; + } else { + swprintf(this->errMsg, errMsgLen, L"factor starts with invalid symbol (number or ( expected)"); return false; + } + return true; // by now +} // PrivateControlValueTable::Factor + +bool PrivateControlValueTable::PixelAtPpemRange(DeltaColor cmdColor, ActParam *actParam, DeltaColor *paramColor) { + int32_t pixelAmount; + ActParam colorParam; + + if (!this->Parameter(actParam)) return false; + if (!AssertPixelAmount(actParam,-maxPixelValue,maxPixelValue,L"Delta amount",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + pixelAmount = actParam->value; + if (pixelAmount == 0) { swprintf(this->errMsg, this->errMsgLen, L"Delta amount cannot be 0"); this->scanner.ErrUnGetSym(); return false; } + if (!this->Parameter(actParam)) return false; + if (actParam->type < ppemN || multipleRangesOfPpemN < actParam->type) { swprintf(this->errMsg, this->errMsgLen, L"Ppem size(s) expected (Example 10..12;14 for ppem sizes 10 through 12 and ppem size 14)"); this->scanner.ErrUnGetSym(); return false; } + *paramColor = cmdColor; + actParam->deltaColor = alwaysDelta; + if (this->scanner.sym == percent) { // optional delta color sub-parameter + this->scanner.GetSym(); + this->Parameter(&colorParam); + if (colorParam.type != naturalN || DeltaColorOfByte((unsigned char)(colorParam.value/one6)) == invalidDelta) { + swprintf(this->errMsg, this->errMsgLen, L"invalid delta color flag (can be " NARROW_STR_FORMAT L" only)",AllDeltaColorBytes()); + this->scanner.ErrUnGetSym(); return false; + } + actParam->deltaColor = DeltaColorOfByte((unsigned char)(colorParam.value/one6)); + if (cmdColor != alwaysDelta && actParam->deltaColor != alwaysDelta) { + swprintf(this->errMsg, this->errMsgLen, L"cannot override delta color specified by a BDELTA or a GDELTA command"); + this->scanner.ErrUnGetSym(); return false; + } + *paramColor = actParam->deltaColor; + } + actParam->value = pixelAmount; // restore + actParam->type = deltaAtRangeOfPpemN; + return true; // by now +} // PrivateControlValueTable::PixelAtPpemRange + +bool PrivateControlValueTable::PpemRange(ActParam *actParam) { + int32_t i; + + actParam->type = ppemN; // for now + for (i = 0; i < maxPpemSize; i++) actParam->deltaPpemSize[i] = false; + if (!this->Range(actParam)) return false; + while (this->scanner.sym == semiColon) { + if (!this->scanner.GetSym()) return false; + if (!this->Range(actParam)) return false; + actParam->type = multipleRangesOfPpemN; // by now + } + for (i = 0; i < maxPpemSize && !actParam->deltaPpemSize[i]; i++); + actParam->value = i; // assign lowest for single ppem size (such as break ppem size) + return true; // by now +} // PrivateControlValueTable::PpemRange + +bool PrivateControlValueTable::Range(ActParam *actParam) { + ActParam lowParam,highParam; + int32_t low,high,i; + + if (!this->Expression(&lowParam)) return false; + if (!AssertNatural(&lowParam,actParam->lowPpemSize+1,actParam->highPpemSize,L"Ppem size",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + actParam->lowPpemSize = low = high = lowParam.value; + if (this->scanner.sym == ellipsis) { + if (!this->scanner.GetSym()) return false; + if (!this->Expression(&highParam)) return false; + if (!AssertNatural(&highParam,actParam->lowPpemSize+1,actParam->highPpemSize,L"Ppem size",this->errMsg, this->errMsgLen)) { this->scanner.ErrUnGetSym(); return false; } + actParam->lowPpemSize = high = highParam.value; + actParam->type = rangeOfPpemN; // by now + } + for (i = low; i <= Min(maxPpemSize-1,high); i++) actParam->deltaPpemSize[i] = true; + return true; // by now +} // PrivateControlValueTable::Range + +bool PrivateControlValueTable::Compile(TextBuffer*source, TextBuffer*prepText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen) { + int32_t i,firstAvailSubAttributeValue[numSubAttributes]; + uint32_t currAttribute; + Attribute *sortedAttributes; + bool memError = false; + wchar_t comment[maxLineSize],dateTime[32]; + + this->legacyCompile = legacyCompile; + this->tempAttributes = NULL; + this->oldSyntax = this->newSyntax = false; // don't know either way at this point + this->errMsg = errMsg; // copy pointer + this->errMsgLen = errMsgLen; // copy length + this->cvtDataValid = this->cvtDataSorted = false; + DefaultSettings(&this->tempSettings); + this->tempData = NewCvtData(); + this->tt = NewTTSourceEngine(); + if (this->tt) this->tt->InitTTEngine(legacyCompile, &memError); + if (!this->cpgmData || !this->tempData || !this->tt || memError) + { + swprintf(this->errMsg, this->errMsgLen, L"Not enough memory to compile control program"); + goto error; + } + + for (i = firstCharGroup; i <= lastCharGroup; i++) + if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)charGroupToStrg[i],(wchar_t *)charGroupToSpacingText[i],group,i,this->errMsg, this->errMsgLen)) goto error; + firstAvailSubAttributeValue[group] = i; + for (i = firstLinkColor; i <= lastLinkColor; i++) + if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)linkColorToStrg[i],L"",color,i,this->errMsg, this->errMsgLen)) goto error; + firstAvailSubAttributeValue[color] = i; + for (i = firstLinkDirection; i <= lastLinkDirection; i++) + if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)linkDirectionToStrg[i],L"",direction,i,this->errMsg, this->errMsgLen)) goto error; + firstAvailSubAttributeValue[direction] = i; + for (i = firstCvtCategory; i <= lastCvtCategory; i++) + if (!Attribute::InsertByName(&this->tempAttributes,true,(wchar_t *)cvtCategoryToStrg[i],L"",category,i,this->errMsg, this->errMsgLen)) goto error; + firstAvailSubAttributeValue[category] = i; + + + this->tt->Emit(L"/* auto-generated pre-program */"); + DateTimeStrg(dateTime); + swprintf(comment,sizeof(comment)/sizeof(wchar_t), L"/* VTT " WIDE_STR_FORMAT L" compiler " WIDE_STR_FORMAT L" */",VTTVersionString,dateTime); + this->tt->Emit(comment); + this->tt->Emit(L""); + + if (!this->scanner.Init(source,NULL,this->errMsg, this->errMsgLen)) goto error; + if (!this->InlineSttmt()) goto error; + while (this->scanner.sym == category || this->scanner.sym == group) { + if (!this->AttributeDeclaration(firstAvailSubAttributeValue)) goto error; + if (!this->InlineSttmt()) goto error; + } + + // need to do fpgmBias BEFORE mandatory settings, but mandatory settings before all other settings + if (!this->legacyCompile && this->scanner.sym == fpgmBias) { + if (!this->SettingsDeclaration()) goto error; + if (!this->InlineSttmt()) goto error; + } + + // mandatory settings + if (this->legacyCompile){ + this->tt->SetAspectRatioFlag(); // not needed + }else { + this->tt->SetGreyScalingFlag(); + } + + // compile settings + while (firstSetting <= this->scanner.sym && this->scanner.sym <= lastSetting) { + if (!this->SettingsDeclaration()) goto error; + if (!this->InlineSttmt()) goto error; + } + + // provide defaults for settings if necessary + if (!this->tempSettings.defined[instructionsOn-firstSetting]) { + swprintf(comment,sizeof(comment)/sizeof(wchar_t), L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[instructionsOn]); this->tt->Emit(comment); + this->tt->INSTCTRL(this->tempSettings.instructionsOnFromPpemSize,this->tempSettings.instructionsOnToPpemSize); + } + if (!this->tempSettings.defined[dropOutCtrlOff-firstSetting] && !this->tempSettings.defined[scanCtrl-firstSetting]) { + swprintf(comment,sizeof(comment)/sizeof(wchar_t), L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[scanCtrl]); this->tt->Emit(comment); + this->tt->SCANCTRL(this->tempSettings.scanCtrlFlags); + } + if (!this->tempSettings.defined[dropOutCtrlOff-firstSetting] && !this->tempSettings.defined[scanType-firstSetting]) { + swprintf(comment,sizeof(comment)/sizeof(wchar_t), L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[scanType]); this->tt->Emit(comment); + this->tt->SCANTYPE(this->tempSettings.scanTypeFlags); + } + if (!this->tempSettings.defined[cvtCutIn-firstSetting]) { + swprintf(comment,sizeof(comment)/sizeof(wchar_t),L"/* " WIDE_STR_FORMAT L" (default) */",keyWord[cvtCutIn]); this->tt->Emit(comment); + this->tt->AssertFreeProjVector(yRomanDir); // so far, this may become aspect-ration dependent, or such like... + this->tt->SCVTCI(this->tempSettings.numCvtCutIns,this->tempSettings.cvtCutInPpemSize,this->tempSettings.cvtCutInValue); + } + + currAttribute = PrivateControlValueTable::PackAttribute(anyGroup,linkAnyColor,linkAnyDir,cvtAnyCategory); + while (this->scanner.sym != eot) { + // if (this->scanner.sym != eol) { + if (!this->CvtDeclaration(&currAttribute)) goto error; + // } + // + // if (this->scanner.sym == eol) { + // if (!this->scanner.GetSym()) goto error; + // } else if (this->scanner.sym != eot) { + // swprintf(errMsg,L"extra text on line"); goto error; + // } + // + } + this->scanner.Term(errPos,errLen); + this->tt->ResetPrepState(); + this->tt->TermTTEngine(this->newSyntax ? prepText : NULL,&memError); + if (memError) + { + swprintf(this->errMsg,this->errMsgLen,L"Not enough memory to compile control program"); + goto error; + } + +#ifdef DEBUGCVT + DebugS(L"LIST OF DEFINED ATTRIBUTES SORTED BY NAME\r"); + DebugS(L"\r"); + Attribute::Dump(this->tempAttributes); + DebugS(L"\r"); +#endif + sortedAttributes = NULL; + if (!Attribute::SortByValue(&sortedAttributes,this->tempAttributes,this->errMsg, this->errMsgLen)) { if (sortedAttributes) delete sortedAttributes; goto error; } + if (this->tempAttributes) delete this->tempAttributes; + this->tempAttributes = sortedAttributes; +#ifdef DEBUGCVT + DebugS(L"LIST OF DEFINED ATTRIBUTES SORTED BY VALUE\r"); + DebugS(L"\r"); + Attribute::Dump(this->tempAttributes); + DebugS(L"\r"); +#endif + + this->cpgmSettings = this->tempSettings; + this->cvtDataValid = true; + for (this->lowestCvtNum = 0; this->lowestCvtNum < maxCvtNum && !(this->tempData[this->lowestCvtNum].flags & cvtDefined); this->lowestCvtNum++); + for (this->highestCvtNum = maxCvtNum-1; this->highestCvtNum >= 0 && !(this->tempData[this->highestCvtNum].flags & cvtDefined); this->highestCvtNum--); + + this->newNumCharGroups = firstAvailSubAttributeValue[group]; + DisposeCvtData(&this->cpgmData); + this->cpgmData = this->tempData; + this->tempData = NULL; + if (this->attributes) delete this->attributes; + this->attributes = this->tempAttributes; + this->tempAttributes = NULL; +#ifdef DEBUGCVT + if (!this->newSyntax) { + DumpOldSyntaxInNewFormat(this); + } +#endif + delete this->tt; this->tt = NULL; + return true; +error: + this->scanner.Term(errPos,errLen); + DisposeCvtData(&this->tempData); + if (this->tempAttributes) { delete this->tempAttributes; this->tempAttributes = NULL; } + if (this->tt) { this->tt->TermTTEngine(NULL,&memError); delete this->tt; this->tt = NULL; } + return false; +} // PrivateControlValueTable::Compile + +bool PrivateControlValueTable::IsControlProgramFormat(void) { + return this->newSyntax; +} // PrivateControlValueTable::IsControlProgramFormat + +bool PrivateControlValueTable::LinearAdvanceWidths(void) { + if (!this->legacyCompile) + { + return this->cpgmSettings.linearAdvanceWidthsFlag > 0; + } + else + { + return false; + } +} // PrivateControlValueTable::LinearAdvanceWidths + + +int32_t PrivateControlValueTable::LowestCvtNum(void) { + return this->cvtDataValid ? this->lowestCvtNum : maxCvtNum; +} // PrivateControlValueTable::LowestCvtNum + +int32_t PrivateControlValueTable::HighestCvtNum(void) { + return this->cvtDataValid ? this->highestCvtNum : -1; +} // PrivateControlValueTable::HighestCvtNum + +int32_t PrivateControlValueTable::LowestCvtIdx(void) { + this->AssertSortedCvt(); + return this->cvtDataSorted ? this->lowestCvtIdx : maxCvtNum; +} // PrivateControlValueTable::LowestCvtIdx + +int32_t PrivateControlValueTable::HighestCvtIdx(void) { + this->AssertSortedCvt(); + return this->cvtDataSorted ? this->highestCvtIdx : -1; +} // PrivateControlValueTable::HighestCvtIdx + +int32_t PrivateControlValueTable::CvtNumOf(int32_t idx) { + this->AssertSortedCvt(); + return this->cvtDataSorted && this->lowestCvtIdx <= idx && idx <= this->highestCvtIdx ? this->cvtKeyOfIdx[idx].num : invalidCvtNum; +} // PrivateControlValueTable::CvtNumOf + +int32_t PrivateControlValueTable::CvtIdxOf(int32_t num) { + this->AssertSortedCvt(); + return this->cvtDataSorted && this->lowestCvtNum <= num && num <= this->highestCvtNum ? this->cvtIdxOfNum[num] : -1; +} // PrivateControlValueTable::CvtIdxOf + +#define Exists(this,cvtNum) (this->cvtDataValid && 0 <= cvtNum && cvtNum <= this->highestCvtNum && this->cpgmData[cvtNum].flags & cvtDefined) +#define AttribExists(this,cvtNum) (Exists(this,cvtNum) && this->cpgmData[cvtNum].flags & attributeDefined) + +bool PrivateControlValueTable::CvtNumExists(int32_t cvtNum) { + return Exists(this,cvtNum); +} // PrivateControlValueTable::CvtNumExists + +bool PrivateControlValueTable::GetCvtValue(int32_t cvtNum, short *cvtValue) { + if (!Exists(this,cvtNum)) return false; + *cvtValue = this->cpgmData[cvtNum].value; + return true; +} // PrivateControlValueTable::GetCvtValue + +bool PrivateControlValueTable::CvtAttributesExist(int32_t cvtNum) { + return AttribExists(this,cvtNum); +} // PrivateControlValueTable::CvtAttributesExist + +bool PrivateControlValueTable::GetCvtAttributes(int32_t cvtNum, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory, bool *relative) { + if (!AttribExists(this,cvtNum)) return false; + PrivateControlValueTable::UnpackAttribute(this->cpgmData[cvtNum].attribute,charGroup,linkColor,linkDirection,cvtCategory); + *relative = (this->cpgmData[cvtNum].flags & relativeValue) != 0; + return true; +} // PrivateControlValueTable::GetCvtAttributes + +int32_t PrivateControlValueTable::NumCharGroups(void) { + return this->cvtDataValid ? this->newNumCharGroups : 0; +} // PrivateControlValueTable::NumCharGroups + +bool PrivateControlValueTable::GetAttributeStrings(int32_t cvtNum, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[], wchar_t relative[], size_t commonStrSize) { + charGroup[0] = linkColor[0] = linkDirection[0] = cvtCategory[0] = relative[0] = L'\0'; + if (!AttribExists(this,cvtNum)) return false; + PrivateControlValueTable::UnpackAttributeStrings(this->cpgmData[cvtNum].attribute,charGroup,linkColor,linkDirection,cvtCategory); + swprintf(relative, commonStrSize, WIDE_STR_FORMAT,this->cpgmData[cvtNum].flags & relativeValue ? L"relative" : L"absolute"); + return true; +} // PrivateControlValueTable::GetAttributeStrings + +bool PrivateControlValueTable::GetCharGroupString(CharGroup charGroup, wchar_t string[]) { + wchar_t errMsg[maxLineSize]; + + string[0] = L'\0'; + if (!this->cvtDataValid || charGroup < 0 || this->newNumCharGroups <= charGroup) return false; + return Attribute::SearchByValue(this->attributes,group,charGroup,string,NULL,errMsg,sizeof(errMsg)/sizeof(wchar_t)); +} // PrivateControlValueTable::GetCharGroupString + +bool PrivateControlValueTable::GetSpacingText(CharGroup charGroup, wchar_t spacingText[]) { + wchar_t errMsg[maxLineSize]; + + spacingText[0] = L'\0'; + if (!this->cvtDataValid || group < 0 || this->newNumCharGroups <= group) return false; + return Attribute::SearchByValue(this->attributes,group,charGroup,NULL,spacingText,errMsg,sizeof(errMsg)/sizeof(wchar_t)); +} // PrivateControlValueTable::GetSpacingText + +#define CompareKey(a,b) ((a).attribute < (b).attribute ? -1 : ((a).attribute > (b).attribute ? +1 : (int32_t)(a).value - (int32_t)(b).value)) + +int32_t PrivateControlValueTable::GetBestCvtMatch(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory, int32_t distance) { + int32_t attempt,firstAttempt,low,high,mid,cmp,lowCvtNum,highCvtNum; + uint32_t lowAttr,highAttr,lowValue,highValue,catMask4cvtDummy; + CvtKey cvtKey,targetKey; + + if (!this->cvtDataValid || cvtCategory == cvtAnyCategory) return invalidCvtNum; + + if (cvtCategory == -1) { + catMask4cvtDummy = ~subAttributeMask; // since cvtCategory occupies lowest 8 bits... + firstAttempt = 1; + cvtCategory = cvtAnyCategory; + } else { + catMask4cvtDummy = 0xffffffff; + firstAttempt = 0; + } + + this->AssertSortedCvt(); + + targetKey.value = (unsigned short)(distance + 0x8000); + for (attempt = firstAttempt; attempt <= 2; attempt++) { + switch (attempt) { + case 0: break; + case 1: cvtCategory = cvtAnyCategory; break; + case 2: linkDirection = linkAnyDir; break; + } + targetKey.attribute = PrivateControlValueTable::PackAttribute(charGroup,linkColor,linkDirection,cvtCategory) & catMask4cvtDummy; + + low = this->lowestCvtIdx; high = this->highestCvtIdx; + do { // binary search + mid = (low + high) >> 1; + cvtKey = this->cvtKeyOfIdx[mid]; + cvtKey.attribute &= catMask4cvtDummy; + cmp = CompareKey(targetKey,cvtKey); + if (!cmp) return this->cvtKeyOfIdx[mid].num; // found + if (cmp > 0) low = mid + 1; else high = mid - 1; + } while (low <= high); + // since by now low > high => lowValue > highValue + lowCvtNum = this->cvtKeyOfIdx[low].num; + highCvtNum = this->cvtKeyOfIdx[high].num; + lowAttr = this->cvtKeyOfIdx[low].attribute & catMask4cvtDummy; + highAttr = this->cvtKeyOfIdx[high].attribute & catMask4cvtDummy; + lowValue = this->cvtKeyOfIdx[low].value; + highValue = this->cvtKeyOfIdx[high].value; + if (lowAttr == targetKey.attribute && highAttr == targetKey.attribute) return lowValue - targetKey.value < targetKey.value - highValue ? lowCvtNum : highCvtNum; + else if (lowAttr == targetKey.attribute) return lowCvtNum; + else if (highAttr == targetKey.attribute) return highCvtNum; + // else there is no cvt in this linkDirection and cvtCategory + } + return invalidCvtNum; // by now +} // PrivateControlValueTable::GetBestCvtMatch + +void PrivateControlValueTable::PutCvtBinary(int32_t size, unsigned char data[]) { + short *cvtData = (short *)data; + int32_t i; + + this->lowestCvtNum = 0; // we don't know any better at this point, plus there are no valid attributes... + this->highestCvtNum = (size >> 1) - 1; + for (i = 0; i <= this->highestCvtNum; i++, cvtData++) this->cpgmData[i].value = SWAPW(*cvtData); +} // PrivateControlValueTable::PutCvtBinary + +void PrivateControlValueTable::GetCvtBinary(int32_t *size, unsigned char data[]) { + short *cvtData = (short *)data; + int32_t i; + + *size = (this->highestCvtNum + 1) << 1; + for (i = 0; i <= this->highestCvtNum; i++, cvtData++) *cvtData = SWAPW(this->cpgmData[i].value); +} // PrivateControlValueTable::GetCvtBinary + +int32_t PrivateControlValueTable::GetCvtBinarySize(void) { + return (this->highestCvtNum + 1) << 1; +} // PrivateControlValueTable::GetCvtBinarySize + +uint32_t PrivateControlValueTable::PackAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory) { + return ((((((uint32_t)charGroup << subAttributeBits) + (uint32_t)linkColor) << subAttributeBits) + (uint32_t)linkDirection) << subAttributeBits) + (uint32_t)cvtCategory; +} // PrivateControlValueTable::PackAttribute + +void PrivateControlValueTable::UnpackAttribute(uint32_t attribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory) { + *cvtCategory = (CvtCategory) (attribute & subAttributeMask); attribute >>= subAttributeBits; + *linkDirection = (LinkDirection)(attribute & subAttributeMask); attribute >>= subAttributeBits; + *linkColor = (LinkColor) (attribute & subAttributeMask); attribute >>= subAttributeBits; + *charGroup = (CharGroup) (attribute & subAttributeMask); +} // PrivateControlValueTable::UnpackAttribute + +void PrivateControlValueTable::UnpackAttributeStrings(uint32_t attribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]) { + CharGroup theGroup; + LinkColor theColor; + LinkDirection theDirection; + CvtCategory theCategory; + wchar_t errMsg[maxLineSize]; + + PrivateControlValueTable::UnpackAttribute(attribute,&theGroup,&theColor,&theDirection,&theCategory); + if (!Attribute::SearchByValue(this->attributes,group, theGroup, charGroup, NULL,errMsg, sizeof(errMsg)/sizeof(wchar_t))) + charGroup[0] = L'\0'; + if (!Attribute::SearchByValue(this->attributes, color, theColor, linkColor, NULL, errMsg, sizeof(errMsg) / sizeof(wchar_t))) + linkColor[0] = L'\0'; + if (!Attribute::SearchByValue(this->attributes, direction, theDirection, linkDirection, NULL, errMsg, sizeof(errMsg) / sizeof(wchar_t))) + linkDirection[0] = L'\0'; + if (!Attribute::SearchByValue(this->attributes, category, theCategory, cvtCategory, NULL, errMsg, sizeof(errMsg) / sizeof(wchar_t))) + cvtCategory[0] = L'\0'; +} // PrivateControlValueTable::UnpackAttributeStrings + +void PrivateControlValueTable::AssertSortedCvt(void) { + int32_t cvtNum,cvtIdx,actCvtValue; + ControlValue *cvt; + uint32_t attribute; + CharGroup aGroup; + LinkColor aColor; + LinkDirection aDir; + CvtCategory aCat; + + if (!this->cvtDataValid) return; // nothing to do + + if (this->cvtDataSorted) return; // we're done + + // produce compacted and sorted array of cvtAttribute|cvtValue keys along with cvtNumber data + cvtIdx = 0; + this->cvtKeyOfIdx[cvtIdx].attribute = 0; + this->cvtKeyOfIdx[cvtIdx].value = 0; // low sentinel + this->cvtKeyOfIdx[cvtIdx].num = invalidCvtNum; // cvtNum irrelevant, just silence BC + cvtIdx++; + for (cvtNum = 0; cvtNum <= this->highestCvtNum; cvtNum++) { + cvt = &this->cpgmData[cvtNum]; + if (cvt->flags & cvtDefined) { // skip the gaps + attribute = cvt->attribute; + PrivateControlValueTable::UnpackAttribute(attribute,&aGroup,&aColor,&aDir,&aCat); + if (aCat == cvtRoundHeight) aCat = cvtSquareHeight; // map them all to square until we determine features automatically + actCvtValue = cvt->value; + if (cvt->flags & relativeValue) { // make it an absolute height for the purpose of finding the best match + actCvtValue += this->cpgmData[cvt->parent].value; + attribute = PrivateControlValueTable::PackAttribute(aGroup,aColor,aDir,aCat); + } + this->cvtKeyOfIdx[cvtIdx].attribute = attribute; + this->cvtKeyOfIdx[cvtIdx].value = (unsigned short)(actCvtValue + 0x8000); // bias cvt values + this->cvtKeyOfIdx[cvtIdx].num = (short)cvtNum; + cvtIdx++; + } + } + this->cvtKeyOfIdx[cvtIdx].attribute = 0xffffffff; + this->cvtKeyOfIdx[cvtIdx].value = 0xffff; // high sentinel + this->cvtKeyOfIdx[cvtIdx].num = invalidCvtNum; // cvtNum irrelevant, just silence BC + cvtIdx++; + this->lowestCvtIdx = 1; + this->highestCvtIdx = cvtIdx-2; + this->SortCvtKeys(0,cvtIdx-1); + for (cvtNum = this->lowestCvtNum; cvtNum <= this->highestCvtNum; cvtNum++) this->cvtIdxOfNum[cvtNum] = invalidCvtNum; // init possibly sparsely populated cvt + for (cvtIdx = this->lowestCvtIdx; cvtIdx <= this->highestCvtIdx; cvtIdx++) this->cvtIdxOfNum[this->cvtKeyOfIdx[cvtIdx].num] = (short)cvtIdx; // setup bijectivity + this->cvtDataSorted = true; +} // PrivateControlValueTable::AssertSortedCvt + +void PrivateControlValueTable::SortCvtKeys(int32_t low, int32_t high) { // quicksort + int32_t i,j; + CvtKey midKey,auxKey; + + if (low < high) { + i = low; j = high; + midKey = this->cvtKeyOfIdx[(i + j) >> 1]; + do { + while (CompareKey(this->cvtKeyOfIdx[i],midKey) < 0) i++; + while (CompareKey(midKey,this->cvtKeyOfIdx[j]) < 0) j--; + if (i <= j) { auxKey = this->cvtKeyOfIdx[i]; this->cvtKeyOfIdx[i] = this->cvtKeyOfIdx[j]; this->cvtKeyOfIdx[j] = auxKey; i++; j--; } + } while (i <= j); + this->SortCvtKeys(low,j); + this->SortCvtKeys(i,high); + } +} // PrivateControlValueTable::SortCvtKeys + +/***** +; Auto CharacterGroup.dat for the font UNICOURC.TTF 2.1 Beta2 +; Numbered columns represent: +; Microsoft Unicode +; Mac code +; Apple Unicode +; Dummy code (a count) +; Character type - YOU MUST CORRECT THIS +; and PostScript Name + +* 0 0 0 0 <-Minimum +* FFEE FF FFEE FFEE <-Maximum + +0020 20 0020 4 OTHER space +0021 21 0021 5 OTHER exclam +0022 22 0022 6 OTHER quotedbl +0023 23 0023 7 OTHER numbersign +0024 24 0024 8 FIGURE dollar +0391 ** 0391 384 UPPERCASE Alpha +*****/ + +/***** +#define maxColumns 6 +#define cglookahead 1 +#define maxColumnSize cvtAttributeStrgLen +#define cgBufSize 0x400 +#define eolCh '\r' +*****/ + +#define unknownUnicode 0xffff + +bool PrivateControlValueTable::DumpControlValueTable(TextBuffer *text) { + int32_t cvtNum,pos; + bool newFormat; + wchar_t dump[maxLineSize],groupStrg[32],colorStrg[32],directionStrg[32],categoryStrg[32],relativeStrg[32]; + + newFormat = this->IsControlProgramFormat(); + for (cvtNum = this->LowestCvtNum(); cvtNum <= this->HighestCvtNum(); cvtNum++) { + short cvtValue; + if (this->GetCvtValue(cvtNum,&cvtValue)) { + pos = swprintf(dump,sizeof(dump)/sizeof(wchar_t),L"%4li: %6i",cvtNum,cvtValue); + if (this->CvtAttributesExist(cvtNum)) { + this->GetAttributeStrings(cvtNum,groupStrg,colorStrg,directionStrg,categoryStrg,relativeStrg,sizeof(relativeStrg)/sizeof(wchar_t)); + pos += swprintf(&dump[pos],sizeof(dump)/sizeof(wchar_t),L" /* " WIDE_STR_FORMAT L" " WIDE_STR_FORMAT L" " WIDE_STR_FORMAT L" " WIDE_STR_FORMAT L" (" WIDE_STR_FORMAT L") */",groupStrg,colorStrg,directionStrg,categoryStrg,relativeStrg); + } + text->AppendLine(dump); + } + } + return true; // by now +} // PrivateControlValueTable::DumpControlValueTable + +bool PrivateControlValueTable::CompileCharGroup(File *from, short platformID, unsigned char toCharGroupOfCharCode[], wchar_t errMsg[], size_t errMsgLen) { + int32_t aGroup,errPos,errLen,row,col,theCol,code[4],platToCol[5] = {2, 1, 2 /* bug??? */, 0, 3}; // plat_Unicode, plat_Macintosh, plat_ISO, plat_MS, unknown case 4 + wchar_t data[2][cvtAttributeStrgLen]; + Scanner scanner; + Attribute *groups; + Symbol subAttribute; + + if (platformID < 0 || 4 < platformID) platformID = plat_MS; + theCol = platToCol[platformID]; + + row = col = 0; + groups = NULL; + for (aGroup = 0; aGroup < this->newNumCharGroups && Attribute::SearchByValue(this->attributes,group,aGroup,data[0],NULL,errMsg, errMsgLen) && Attribute::InsertByName(&groups,false,data[0],NULL,group,aGroup,errMsg, errMsgLen); aGroup++); + if (aGroup < this->newNumCharGroups) goto error; + if (!scanner.Init(NULL,from,errMsg,errMsgLen)) goto error; + while (scanner.sym != eot) { + col = 0; + while (col < 4 && (scanner.sym == (col < 3 ? hexadecimal : natural) || scanner.sym == times)) { + code[col] = scanner.sym == times ? unknownUnicode : scanner.value; + col++; + if (!scanner.GetSym()) goto error; + } + if (col < 4) { swprintf(errMsg,errMsgLen,WIDE_STR_FORMAT L" number expected",col < 3 ? L"hexadecimal" : L"decimal"); goto error; } + while (col < 6 && scanner.sym == ident) { + AssignString(data[col-4],scanner.literal,cvtAttributeStrgLen); + col++; + if (!scanner.GetSym()) goto error; + } + if (col < 6) { swprintf(errMsg,errMsgLen,WIDE_STR_FORMAT L" expected",col < 5 ? L"character group" : L"postscript name"); goto error; } + if (!Attribute::SearchByName(groups,data[0],NULL,&subAttribute,&aGroup,errMsg,errMsgLen) || subAttribute != group) goto error; + if (code[theCol] != unknownUnicode) toCharGroupOfCharCode[code[theCol]] = (unsigned char)aGroup; + row++; + } + scanner.Term(&errPos,&errLen); + if (groups) delete groups; + return true; +error: + swprintf(&errMsg[STRLENW(errMsg)],errMsgLen,L" on line %li, column %li",row,col); + scanner.Term(&errPos,&errLen); + if (groups) delete groups; + return false; +} // PrivateControlValueTable::CompileCharGroup + + +ControlValueTable *NewControlValueTable(void) { + return new PrivateControlValueTable; +} // NewControlValueTable + +short PackCvtHexAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory) { + return charGroupToInt[charGroup] << groupPos | linkColorToInt[linkColor] << colorPos | linkDirectionToInt[linkDirection] << dirPos | cvtCategoryToInt[cvtCategory] << catPos; +} // PackCvtHexAttribute + +void UnpackCvtHexAttribute(short hexAttribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory) { + *cvtCategory = intToCvtCategory[ hexAttribute >> catPos & catMask]; + *linkDirection = intToLinkDirection[hexAttribute >> dirPos & dirMask]; + *linkColor = intToLinkColor[ hexAttribute >> colorPos & colorMask]; + *charGroup = intToCharGroup[ hexAttribute >> groupPos & groupMask]; +} // UnpackCvtHexAttribute + +void UnpackCvtHexAttributeStrings(short hexAttribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]) { + CharGroup group; + LinkColor color; + LinkDirection direction; + CvtCategory category; + + UnpackCvtHexAttribute(hexAttribute,&group,&color,&direction,&category); + STRCPYW(charGroup,charGroupToStrg[group]); + STRCPYW(linkColor,linkColorToStrg[color]); + STRCPYW(linkDirection,linkDirectionToStrg[direction]); + STRCPYW(cvtCategory,cvtCategoryToStrg[category]); +} // UnpackCvtHexAttributeStrings diff --git a/src/CvtManager.h b/src/CvtManager.h index d258a5e..faf5cb2 100644 --- a/src/CvtManager.h +++ b/src/CvtManager.h @@ -1,77 +1,77 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef CvtManager_dot_h -#define CvtManager_dot_h - -typedef enum { anyGroup = 0, otherCase, upperCase, lowerCase, figureCase, nonLatinCase, reservedCase2, reservedCase3 } CharGroup; - - -// reserved character groups because of the way the char group table is encoded, cf. TTFont.cpp, charGroupToIntInFile and intInFileToCharGroup - -#define firstCharGroup anyGroup -#define lastCharGroup reservedCase3 -#define numCharGroups (CharGroup)(lastCharGroup - firstCharGroup + 1) - -typedef enum { linkAnyColor, linkBlack, linkGrey, linkWhite } LinkColor; -#define firstLinkColor linkAnyColor -#define lastLinkColor linkWhite -#define numLinkColors (LinkColor)(lastLinkColor - firstLinkColor + 1) - -typedef enum { linkAnyDir, linkX, linkY, linkDiag } LinkDirection; -#define firstLinkDirection linkAnyDir -#define lastLinkDirection linkDiag -#define numLinkDirections (LinkDirection)(lastLinkDirection - firstLinkDirection + 1) - -typedef enum {cvtAnyCategory = 0, cvtDistance, cvtStroke, cvtRound, - cvtLsb, cvtRsb, cvtBlackBody, cvtSerifThin, cvtSerifHeight, cvtSerifExt, cvtSerifCurveHeight, cvtSerifOther, cvtSerifBottom, - cvtSquareHeight, cvtRoundHeight, cvtItalicRun, cvtItalicRise, - cvtBendRadius, cvtBranchBendRadius, cvtScoopDepth, cvtNewStraightStroke, cvtNewDiagStroke, cvtNewAnyCategory} CvtCategory; -#define firstCvtCategory cvtAnyCategory -#define lastCvtCategory cvtNewAnyCategory -#define numUsedCvtCategories (CvtCategory)(cvtRound - firstCvtCategory + 1) -#define numCvtCategories (CvtCategory)(lastCvtCategory - firstCvtCategory + 1) - -#define maxCvtNum 0x1000L // get past FontLab's Else - -#define invalidCvtNum (-1) - -#define cvtAttributeStrgLen 64 - -class ControlValueTable { -public: - ControlValueTable(void) {} - virtual ~ControlValueTable(void) {} - virtual bool Compile(TextBuffer *source, TextBuffer *prepText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]) = 0; - virtual bool IsControlProgramFormat(void) = 0; - virtual bool LinearAdvanceWidths(void) = 0; - virtual int32_t LowestCvtNum(void) = 0; - virtual int32_t HighestCvtNum(void) = 0; - virtual int32_t LowestCvtIdx(void) = 0; - virtual int32_t HighestCvtIdx(void) = 0; - virtual int32_t CvtNumOf(int32_t idx) = 0; - virtual int32_t CvtIdxOf(int32_t num) = 0; - virtual bool CvtNumExists(int32_t cvtNum) = 0; - virtual bool GetCvtValue(int32_t cvtNum, short *cvtValue) = 0; - virtual bool CvtAttributesExist(int32_t cvtNum) = 0; // entered a cvt "comment"? - virtual bool GetCvtAttributes(int32_t cvtNum, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory, bool *relative) = 0; - virtual bool GetAttributeStrings(int32_t cvtNum, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[], wchar_t relative[]) = 0; - virtual int32_t NumCharGroups(void) = 0; - virtual bool GetCharGroupString(CharGroup charGroup, wchar_t string[]) = 0; - virtual bool GetSpacingText(CharGroup charGroup, wchar_t spacingText[]) = 0; - virtual int32_t GetBestCvtMatch(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory, int32_t distance) = 0; // returns invalidCvtNum if no match - virtual void PutCvtBinary(int32_t size, unsigned char data[]) = 0; - virtual void GetCvtBinary(int32_t *size, unsigned char data[]) = 0; - virtual int32_t GetCvtBinarySize(void) = 0; - virtual bool DumpControlValueTable(TextBuffer *text) = 0; - virtual bool CompileCharGroup(File *from, short platformID, unsigned char toCharGroupOfCharCode[], wchar_t errMsg[]) = 0; -private: -}; - -ControlValueTable *NewControlValueTable(void); -short PackCvtHexAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory); -void UnpackCvtHexAttribute(short hexAttribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory); -void UnpackCvtHexAttributeStrings(short hexAttribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]); - -#endif // CvtManager_dot_h - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef CvtManager_dot_h +#define CvtManager_dot_h + +typedef enum { anyGroup = 0, otherCase, upperCase, lowerCase, figureCase, nonLatinCase, reservedCase2, reservedCase3 } CharGroup; + + +// reserved character groups because of the way the char group table is encoded, cf. TTFont.cpp, charGroupToIntInFile and intInFileToCharGroup + +#define firstCharGroup anyGroup +#define lastCharGroup reservedCase3 +#define numCharGroups (CharGroup)(lastCharGroup - firstCharGroup + 1) + +typedef enum { linkAnyColor, linkBlack, linkGrey, linkWhite } LinkColor; +#define firstLinkColor linkAnyColor +#define lastLinkColor linkWhite +#define numLinkColors (LinkColor)(lastLinkColor - firstLinkColor + 1) + +typedef enum { linkAnyDir, linkX, linkY, linkDiag } LinkDirection; +#define firstLinkDirection linkAnyDir +#define lastLinkDirection linkDiag +#define numLinkDirections (LinkDirection)(lastLinkDirection - firstLinkDirection + 1) + +typedef enum {cvtAnyCategory = 0, cvtDistance, cvtStroke, cvtRound, + cvtLsb, cvtRsb, cvtBlackBody, cvtSerifThin, cvtSerifHeight, cvtSerifExt, cvtSerifCurveHeight, cvtSerifOther, cvtSerifBottom, + cvtSquareHeight, cvtRoundHeight, cvtItalicRun, cvtItalicRise, + cvtBendRadius, cvtBranchBendRadius, cvtScoopDepth, cvtNewStraightStroke, cvtNewDiagStroke, cvtNewAnyCategory} CvtCategory; +#define firstCvtCategory cvtAnyCategory +#define lastCvtCategory cvtNewAnyCategory +#define numUsedCvtCategories (CvtCategory)(cvtRound - firstCvtCategory + 1) +#define numCvtCategories (CvtCategory)(lastCvtCategory - firstCvtCategory + 1) + +#define maxCvtNum 0x1000L // get past FontLab's Else + +#define invalidCvtNum (-1) + +#define cvtAttributeStrgLen 64 + +class ControlValueTable { +public: + ControlValueTable(void) {} + virtual ~ControlValueTable(void) {} + virtual bool Compile(TextBuffer *source, TextBuffer *prepText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen) = 0; + virtual bool IsControlProgramFormat(void) = 0; + virtual bool LinearAdvanceWidths(void) = 0; + virtual int32_t LowestCvtNum(void) = 0; + virtual int32_t HighestCvtNum(void) = 0; + virtual int32_t LowestCvtIdx(void) = 0; + virtual int32_t HighestCvtIdx(void) = 0; + virtual int32_t CvtNumOf(int32_t idx) = 0; + virtual int32_t CvtIdxOf(int32_t num) = 0; + virtual bool CvtNumExists(int32_t cvtNum) = 0; + virtual bool GetCvtValue(int32_t cvtNum, short *cvtValue) = 0; + virtual bool CvtAttributesExist(int32_t cvtNum) = 0; // entered a cvt "comment"? + virtual bool GetCvtAttributes(int32_t cvtNum, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory, bool *relative) = 0; + virtual bool GetAttributeStrings(int32_t cvtNum, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[], wchar_t relative[], size_t commonStrSize) = 0; + virtual int32_t NumCharGroups(void) = 0; + virtual bool GetCharGroupString(CharGroup charGroup, wchar_t string[]) = 0; + virtual bool GetSpacingText(CharGroup charGroup, wchar_t spacingText[]) = 0; + virtual int32_t GetBestCvtMatch(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory, int32_t distance) = 0; // returns invalidCvtNum if no match + virtual void PutCvtBinary(int32_t size, unsigned char data[]) = 0; + virtual void GetCvtBinary(int32_t *size, unsigned char data[]) = 0; + virtual int32_t GetCvtBinarySize(void) = 0; + virtual bool DumpControlValueTable(TextBuffer *text) = 0; + virtual bool CompileCharGroup(File *from, short platformID, unsigned char toCharGroupOfCharCode[], wchar_t errMsg[], size_t errMsgLen) = 0; +private: +}; + +ControlValueTable *NewControlValueTable(void); +short PackCvtHexAttribute(CharGroup charGroup, LinkColor linkColor, LinkDirection linkDirection, CvtCategory cvtCategory); +void UnpackCvtHexAttribute(short hexAttribute, CharGroup *charGroup, LinkColor *linkColor, LinkDirection *linkDirection, CvtCategory *cvtCategory); +void UnpackCvtHexAttributeStrings(short hexAttribute, wchar_t charGroup[], wchar_t linkColor[], wchar_t linkDirection[], wchar_t cvtCategory[]); + +#endif // CvtManager_dot_h + diff --git a/src/File.cpp b/src/File.cpp index f32567d..a01c766 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -1,175 +1,175 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//#define NOMINMAX -//#include -#define _CRT_SECURE_NO_WARNINGS - -#include - -#include "pch.h" -#include - -#define IsUnicode(b) ((b[0] == 0xFF) && (b[1] == 0xFE)) -#define IsUnicodeBig(b) ((b[0] == 0xFE) && (b[1] == 0xFF)) -#define IsUTF8(b) ((b[0] == 0xEF) && (b[1] == 0xBB) && (b[2] == 0xBF)) - -bool File::Error() { - return m_error; -} // File::Error - -File::File(void) { - // this->m_hfile = INVALID_HANDLE_VALUE; - this->m_hfile = nullptr; - -} // File::File - -File::~File(void) { - //if (this->m_hfile != INVALID_HANDLE_VALUE) - // this->Close(false); - if (this->m_hfile != nullptr) - this->Close(false); -} // File::~File - -void File::OpenOld(const std::string& fileName) { - //m_hfile = CreateFileA(fileName, GENERIC_READ /* | GENERIC_WRITE */, FILE_SHARE_READ, NULL, - // OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - //m_error = (m_hfile == INVALID_HANDLE_VALUE); - m_fileName = fileName; - m_hfile = fopen(fileName.c_str(), "rb"); - m_error = (m_hfile == nullptr); - -} // File::OpenOld - -void File::OpenNew(const std::string& fileName) { - //m_hfile = CreateFileA(fileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, - // CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - //m_error = (m_hfile == INVALID_HANDLE_VALUE); - m_fileName = fileName; - m_hfile = fopen(fileName.c_str(), "wb"); - m_error = (m_hfile == nullptr); -} // File::OpenNew - -int32_t File::Length(void) { - //return (m_hfile != INVALID_HANDLE_VALUE) ? GetFileSize(m_hfile, NULL) : 0; - struct stat st; - stat(m_fileName.c_str(), &st); - return static_cast(st.st_size); -} // File::Length - -void File::SetPos(int32_t pos, bool truncate) { - //SetFilePointer(m_hfile, pos, NULL, FILE_BEGIN); - //if (truncate) - // SetEndOfFile(m_hfile); - fseek((FILE*)m_hfile, SEEK_SET, pos); -} // File::SetPos - -int32_t File::GetPos(void) { - return static_cast(ftell((FILE*)m_hfile)); -} // File::GetPos - -void File::ReadBytes(int32_t numBytes, void *buffer) { - //if (m_hfile != INVALID_HANDLE_VALUE) { - // DWORD cb = 0; - // m_error = !ReadFile(m_hfile, buffer, numBytes, &cb, NULL); - //} - size_t size = 0; - if (m_hfile != nullptr) - { - size = fread(buffer, 1, numBytes, (FILE*)m_hfile); - } -} // File::ReadBytes - -void File::ReadUnicode(int32_t *len, wchar_t **text) -{ - assert(false); - /*if (m_hfile != INVALID_HANDLE_VALUE) { - DWORD cb; - BYTE start[4]; - - m_error = !ReadFile(m_hfile, start, 4, &cb, NULL); - assert(4 == cb); - if (m_error) return; - - int32_t fileLen = this->Length(); - - if (IsUnicode(start) || IsUnicodeBig(start)) - { - SetPos(2, false); - - *text = (wchar_t*)NewP(fileLen); - if (*text == NULL) - { - m_error = true; - return; - } - - m_error = !ReadFile(m_hfile, *text, fileLen - sizeof(wchar_t), &cb, NULL); - assert(fileLen - sizeof(wchar_t) == cb); - if (m_error) return; - - *len = (fileLen - sizeof(wchar_t)) >> 1; - (*text)[*len] = 0; - - if (IsUnicodeBig(start)) - { - for (int32_t i = 0; i < *len; i++) - { - (*text)[i] = SWAPW((*text)[i]); - } - } - } - else - { - SetPos(0, false); - - char* b = (char*)NewP(fileLen + sizeof(char)); - if (b == NULL) - { - m_error = true; - return; - } - - m_error = !ReadFile(m_hfile, b, fileLen, &cb, NULL); - assert(fileLen == cb); - if (m_error) return; - - *len = MultiByteToWideChar(CP_UTF8, 0, b, fileLen, NULL, 0); - - *text = (wchar_t *)NewP((*len + 1) * sizeof(wchar_t)); - if (*text == NULL) return; - - MultiByteToWideChar(CP_UTF8, 0, b, fileLen, *text, *len); - - DisposeP((void**)&b); - } - } - */ -} - -void File::WriteBytes(int32_t numBytes, void *buffer) -{ - //if (m_hfile != INVALID_HANDLE_VALUE) { - // DWORD cb = 0; - // m_error = !WriteFile(m_hfile, buffer, numBytes, &cb, NULL); - //} - size_t size = 0; - if (m_hfile != nullptr) - { - size = fwrite(buffer, 1, numBytes, (FILE*)m_hfile); - } -} // File::WriteBytes - -void File::Close(bool truncate) { - //if (!this->m_error && truncate) this->SetPos(this->GetPos(), true); - //if (this->m_hfile != INVALID_HANDLE_VALUE) - // this->m_error = !CloseHandle(this->m_hfile); - //else - // this->m_error = true; - if (m_hfile != nullptr) - { - fclose((FILE*)m_hfile); - } - this->m_hfile = nullptr; -} // File::Close - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//#define NOMINMAX +//#include +#define _CRT_SECURE_NO_WARNINGS + +#include + +#include "pch.h" +#include + +#define IsUnicode(b) ((b[0] == 0xFF) && (b[1] == 0xFE)) +#define IsUnicodeBig(b) ((b[0] == 0xFE) && (b[1] == 0xFF)) +#define IsUTF8(b) ((b[0] == 0xEF) && (b[1] == 0xBB) && (b[2] == 0xBF)) + +bool File::Error() { + return m_error; +} // File::Error + +File::File(void) { + // this->m_hfile = INVALID_HANDLE_VALUE; + this->m_hfile = nullptr; + +} // File::File + +File::~File(void) { + //if (this->m_hfile != INVALID_HANDLE_VALUE) + // this->Close(false); + if (this->m_hfile != nullptr) + this->Close(false); +} // File::~File + +void File::OpenOld(const std::string& fileName) { + //m_hfile = CreateFileA(fileName, GENERIC_READ /* | GENERIC_WRITE */, FILE_SHARE_READ, NULL, + // OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + //m_error = (m_hfile == INVALID_HANDLE_VALUE); + m_fileName = fileName; + m_hfile = fopen(fileName.c_str(), "rb"); + m_error = (m_hfile == nullptr); + +} // File::OpenOld + +void File::OpenNew(const std::string& fileName) { + //m_hfile = CreateFileA(fileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, + // CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + //m_error = (m_hfile == INVALID_HANDLE_VALUE); + m_fileName = fileName; + m_hfile = fopen(fileName.c_str(), "wb"); + m_error = (m_hfile == nullptr); +} // File::OpenNew + +int32_t File::Length(void) { + //return (m_hfile != INVALID_HANDLE_VALUE) ? GetFileSize(m_hfile, NULL) : 0; + struct stat st; + stat(m_fileName.c_str(), &st); + return static_cast(st.st_size); +} // File::Length + +void File::SetPos(int32_t pos, bool truncate) { + //SetFilePointer(m_hfile, pos, NULL, FILE_BEGIN); + //if (truncate) + // SetEndOfFile(m_hfile); + fseek((FILE*)m_hfile, SEEK_SET, pos); +} // File::SetPos + +int32_t File::GetPos(void) { + return static_cast(ftell((FILE*)m_hfile)); +} // File::GetPos + +void File::ReadBytes(int32_t numBytes, void *buffer) { + //if (m_hfile != INVALID_HANDLE_VALUE) { + // DWORD cb = 0; + // m_error = !ReadFile(m_hfile, buffer, numBytes, &cb, NULL); + //} + size_t size = 0; + if (m_hfile != nullptr) + { + size = fread(buffer, 1, numBytes, (FILE*)m_hfile); + } +} // File::ReadBytes + +void File::ReadUnicode(int32_t *len, wchar_t **text) +{ + assert(false); + /*if (m_hfile != INVALID_HANDLE_VALUE) { + DWORD cb; + BYTE start[4]; + + m_error = !ReadFile(m_hfile, start, 4, &cb, NULL); + assert(4 == cb); + if (m_error) return; + + int32_t fileLen = this->Length(); + + if (IsUnicode(start) || IsUnicodeBig(start)) + { + SetPos(2, false); + + *text = (wchar_t*)NewP(fileLen); + if (*text == NULL) + { + m_error = true; + return; + } + + m_error = !ReadFile(m_hfile, *text, fileLen - sizeof(wchar_t), &cb, NULL); + assert(fileLen - sizeof(wchar_t) == cb); + if (m_error) return; + + *len = (fileLen - sizeof(wchar_t)) >> 1; + (*text)[*len] = 0; + + if (IsUnicodeBig(start)) + { + for (int32_t i = 0; i < *len; i++) + { + (*text)[i] = SWAPW((*text)[i]); + } + } + } + else + { + SetPos(0, false); + + char* b = (char*)NewP(fileLen + sizeof(char)); + if (b == NULL) + { + m_error = true; + return; + } + + m_error = !ReadFile(m_hfile, b, fileLen, &cb, NULL); + assert(fileLen == cb); + if (m_error) return; + + *len = MultiByteToWideChar(CP_UTF8, 0, b, fileLen, NULL, 0); + + *text = (wchar_t *)NewP((*len + 1) * sizeof(wchar_t)); + if (*text == NULL) return; + + MultiByteToWideChar(CP_UTF8, 0, b, fileLen, *text, *len); + + DisposeP((void**)&b); + } + } + */ +} + +void File::WriteBytes(int32_t numBytes, void *buffer) +{ + //if (m_hfile != INVALID_HANDLE_VALUE) { + // DWORD cb = 0; + // m_error = !WriteFile(m_hfile, buffer, numBytes, &cb, NULL); + //} + size_t size = 0; + if (m_hfile != nullptr) + { + size = fwrite(buffer, 1, numBytes, (FILE*)m_hfile); + } +} // File::WriteBytes + +void File::Close(bool truncate) { + //if (!this->m_error && truncate) this->SetPos(this->GetPos(), true); + //if (this->m_hfile != INVALID_HANDLE_VALUE) + // this->m_error = !CloseHandle(this->m_hfile); + //else + // this->m_error = true; + if (m_hfile != nullptr) + { + fclose((FILE*)m_hfile); + } + this->m_hfile = nullptr; +} // File::Close + diff --git a/src/File.h b/src/File.h index 967dd56..f92567f 100644 --- a/src/File.h +++ b/src/File.h @@ -1,29 +1,29 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -class File -{ -public: - bool Error(void); - - explicit File(void); - virtual ~File(void); - virtual void OpenOld(const std::string &fileName); - virtual void OpenNew(const std::string &fileName); - virtual int32_t Length(void); - virtual void SetPos(int32_t pos, bool truncate); - virtual int32_t GetPos(void); - virtual void ReadBytes(int32_t numBytes, void *buffer); - virtual void ReadUnicode(int32_t *len, wchar_t **text); - virtual void WriteBytes(int32_t numBytes, void *buffer); - virtual void Close(bool truncate); -private: - bool m_error; - void *m_hfile; - std::string m_fileName; -}; - - - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +class File +{ +public: + bool Error(void); + + explicit File(void); + virtual ~File(void); + virtual void OpenOld(const std::string &fileName); + virtual void OpenNew(const std::string &fileName); + virtual int32_t Length(void); + virtual void SetPos(int32_t pos, bool truncate); + virtual int32_t GetPos(void); + virtual void ReadBytes(int32_t numBytes, void *buffer); + virtual void ReadUnicode(int32_t *len, wchar_t **text); + virtual void WriteBytes(int32_t numBytes, void *buffer); + virtual void Close(bool truncate); +private: + bool m_error; + void *m_hfile; + std::string m_fileName; +}; + + + diff --git a/src/Memory.cpp b/src/Memory.cpp index df5c112..6d7d154 100644 --- a/src/Memory.cpp +++ b/src/Memory.cpp @@ -1,20 +1,19 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include // swprintf -#include -#include "Memory.h" - -void *NewP(size_t bytes) { - void *ptr = malloc(bytes); - return ptr; -} // NewP - -void DisposeP(void** ptr) { - free(*ptr); - *ptr = NULL; -} // DisposeP - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define _CRT_SECURE_NO_DEPRECATE + +#include // swprintf +#include +#include "Memory.h" + +void *NewP(size_t bytes) { + void *ptr = malloc(bytes); + return ptr; +} // NewP + +void DisposeP(void** ptr) { + free(*ptr); + *ptr = NULL; +} // DisposeP + diff --git a/src/TMTParser.cpp b/src/TMTParser.cpp index e1060f9..930842e 100644 --- a/src/TMTParser.cpp +++ b/src/TMTParser.cpp @@ -1,2280 +1,2320 @@ -/***** - - TMTParser.c - New TypeMan Talk Compiler - Parser - - - Copyright (c) Microsoft Corporation. - Licensed under the MIT License. - -*****/ -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include // wcslen -#include // printf - -#include "pch.h" -#include "List.h" -#include "TTEngine.h" -#include "TTGenerator.h" -#include "TMTParser.h" - -#define idents invalid -#define compLogics 2L -#define phases 4L -#define serifs 5L -#define idLen 32L -#define maxFPs 10L -#define maxAngle 18000L /* half a full circle at 100 units per degree */ -#define shortMax 0x7fffL -#define special 0 -#define lexical 1 -#define syntactical 2 -#define contextual 3 -#define chLookAhead 2 - -#define WhiteSpace(self) (L'\x0' < self->ch && self->ch <= L' ') -#define InitComment(self) (self->ch == L'/' && self->ch2 == L'*') -#define TermComment(self) (self->ch == L'*' && self->ch2 == L'/') -#define Numeric(ch) (L'0' <= (ch) && (ch) <= L'9') -#define Alpha(ch) ((L'A' <= (ch) && (ch) <= L'Z') || (L'a' <= (ch) && (ch) <= L'z')) -#define Command(self) (align <= (self)->sym && (self)->sym <= invalid) -#define InitFlag(self) (leftDir <= (self)->sym && (self)->sym <= postRound) -#define InitParam(self) (invalid <= (self)->sym && (self)->sym <= leftDir) // leftDir included because it doubles up as lessThan in the minDistGeneral parameter -#define Separator(self) (period <= (self)->sym && (self)->sym <= semiColon) -#define InterpolateCmd(cmd) ((cmd) == xInterpolate || (cmd) == xInterpolate0 || (cmd) == xInterpolate1 || (cmd) == xIPAnchor || (cmd) == yInterpolate || (cmd) == yInterpolate0 || (cmd) == yInterpolate1 || (cmd) == yIPAnchor) -#define maxGenerators 3 - -void TMTParser::Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]) { /* abstract */ } -#if _DEBUG -void TMTParser::RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[]) { /* abstract */ } -#endif -void TMTParser::InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]) { /* abstract */ } -void TMTParser::TermTMTParser(void) { /* abstract */ } -TMTParser::TMTParser(void) { /* abstract */ } -TMTParser::~TMTParser(void) { /* abstract */ } - -/* note: this and all following enumeration typedefs should be assumed to be ordered */ -typedef enum { align = 0, asM, autoHinter, - beginCodePath, // COM - call, compilerLogic, cvtAllocation, cvtLogic, - dAlign, diagEndCtrl, diagonalMT, diagSerifs, dStroke, - endCodePath, // COM - fixDStrokes, glyphStrokeAngle, grabHereInX, height, InLine, intersect, iStroke, mainStrokeAngle, processXSymmetrically, processYSymmetrically, quit, - resIIPDDist, resIIPDLink, // COM - resXAnchor, resXDDist, resXDist, resXDLink, resXIPAnchor, resXIPDDDist, resXIPDDist, resXIPDDLink, resXIPDist, resXIPDLink, resXIPLink, resXLink, // COM - resYAnchor, resYDDist, resYDist, resYDLink, resYIPAnchor, resYIPDDDist, resYIPDDist, resYIPDDLink, resYIPDist, resYIPDLink, resYIPLink, resYLink, // COM - scoop, serif, setItalicStrokeAngle, setItalicStrokePhase, smooth, stroke, tail, tweakMetrics, vacuFormLimit, vacuFormRound, - //xAlign, xAnchor, xBDelta, xDelta, xDiagonal, xDist, xDoubleGrid, xDownToGrid, xGDelta, xHalfGrid, xInterpolate, xInterpolate0, xInterpolate1, xIPAnchor, xLink, xMove, xNoRound, xRound, xShift, xSmooth, xStem, xStroke, xUpToGrid, - xAlign, xAnchor, xBDelta, xCDelta, xDelta, xDiagonal, xDist, xDoubleGrid, xDownToGrid, xGDelta, xHalfGrid, xInterpolate, xInterpolate0, xInterpolate1, xIPAnchor, xLink, xMove, xNoRound, xRound, xShift, xSmooth, xStem, xStroke, xUpToGrid, // MODIFY GREGH - //yAlign, yAnchor, yBDelta, yDelta, yDiagonal, yDist, yDoubleGrid, yDownToGrid, yGDelta, yHalfGrid, yInterpolate, yInterpolate0, yInterpolate1, yIPAnchor, yLink, yMove, yNoRound, yRound, yShift, ySmooth, yStem, yStroke, yUpToGrid, - yAlign, yAnchor, yBDelta, yCDelta, yDelta, yDiagonal, yDist, yDoubleGrid, yDownToGrid, yGDelta, yHalfGrid, yInterpolate, yInterpolate0, yInterpolate1, yIPAnchor, yLink, yMove, yNoRound, yRound, yShift, ySmooth, yStem, yStroke, yUpToGrid, // MODIFY GREGH - invalid, - leftParen, plus, minus, natural, rational, literal, aT, atLeast, - upDir, // COM - leftDir, /* InitParam (including above invalid) */ - rightDir, italAngle, adjItalAngle, - optStroke, optStrokeLeftBias, optStrokeRightBias, // COM - postRound, /* leftDir through postRound: Flags() */ - rightParen, timeS, - period, ellipsis, - colon, // COM - percent, - comma, semiColon, /* period through semiColon: Separator() */ - eot - } Symbol; - -typedef enum { voidParam = 0, - anyN, knotN, - knotNttvOpt, knotNttvOptXY, // COM - cvtN, compLogicN, cvtLogicN, phaseN, angle100N, colorN, serifN, curveN, radiusN, - rationalN, posRationalN, - ppemSize, ppemN, rangeOfPpemN, - rangeOfPpemNcolorOpt, - anyS, - minDistFlagOnly, minDistGeneral, - dirFlag, angleFlag, - strokeFlag, // COM - postRoundFlag} ParamType; - -// #define knotNttvOpt knotN // !COM -// #define knotNttvOptXY knotN // !COM - -typedef enum { mand, opt, optR} Presence; - -typedef struct { - ParamType type; - Presence pres; -} FormParam; - -typedef struct { - ParamType type; - int32_t numValue; - wchar_t *litValue; // pointer to parser's litValue for memory efficiency, since we don't have more than 1 string parameter per TMT command - short minDists; - int32_t jumpPpemSize[maxMinDist],pixelSize[maxMinDist]; - bool deltaPpemSize[maxPpemSize]; // here we have possibly more than one rangeOfPpemN parameter, but could implement bit vectors... - DeltaColor deltaColor; // alwaysDelta, blackDelta, greyDelta, ..., same for the entire bit vector deltaPpemSize above - bool hasTtvOverride; - TTVectorDesc ttvOverride; // for diagDir and perpDiagDir -} ActParam; - -class Height : public ListElem { -public: - Height(void); - virtual ~Height(void); - short of; - short cvtOverride; -}; - -class Partner : public ListElem { -public: - Partner(void); - virtual ~Partner(void); - LinkDirection direction; - CvtCategory category; - short of,with; - short cvtOverride; -}; - -Height::Height(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // Height::Height - -Height::~Height(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // Height::~Height - -Partner::Partner(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // Partner::Partner - -Partner::~Partner(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // Partner::~Partner - -class TMTSourceParser : public TMTParser { -public: - virtual void Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]); -#if _DEBUG - virtual void RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[]); -#endif - virtual void InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]); - virtual void TermTMTParser(void); - TMTSourceParser(void); - virtual ~TMTSourceParser(void); -private: - int32_t errPos,symLen; - wchar_t errMsg[maxLineSize]; - TextBuffer *talkText; - TrueTypeFont *font; - TrueTypeGlyph *glyph; - LinearListStruct *heights,*partners; - short knots; - double tanStraightAngle; - bool italicStrokePhase,italicStrokeAngle,mainStrokeAngle,glyphStrokeAngle; - short generators; - TTGenerator *gen[maxGenerators]; - bool changedSrc; - int32_t pos,prevPos,prevPrevPos; // prevPos = position previous to starting the scanning of current symbol - wchar_t ch,ch2; // 2-char look-ahead - Symbol sym; // 1-symbol look-ahead - int32_t numValue; - bool legacyCompile; - wchar_t litValue[maxAsmSize]; - - short actParams; - ActParam actParam[maxParams]; - int32_t paramPos[maxParams + 1]; // +1 needed to avoid out of bounds error if at max parameters - - bool MakeProjFreeVector(bool haveFlag, int32_t flagValue, bool y, ActParam *parent, ActParam child[], int32_t children, ProjFreeVector *projFreeVector, wchar_t errMsg[]); - - virtual void Dispatch(Symbol cmd, short params, ActParam param[], wchar_t errMsg[]); - - Height *TheHeight(short at); - void RegisterHeight(short at, short cvt); - Partner *ThePartner(bool y, short from, short to); - void RegisterPartner(short from, short to, bool y, bool round, short cvt); - - /***** Scanner *****/ - virtual void GetCh(void); - virtual void SkipComment(void); - virtual void SkipWhiteSpace(bool includingComments); - virtual void GetNumber(void); - virtual void GetIdent(void); - virtual void GetLiteral(void); - virtual void GetSym(void); - - void Delete(int32_t pos, int32_t len); - void Insert(int32_t pos, const wchar_t strg[]); - void ReplAtCurrPos(short origLen, const wchar_t repl[]); - - /***** Parser *****/ - virtual void XFormToNewSyntax(void); - virtual void Flag(ActParam *actParam); - virtual void Parameter(ActParam *actParam); - virtual void MatchParameter(FormParam *formParams, short *formParamNum, ParamType *actParamType); - virtual void ValidateParameter(ActParam *actParam); - virtual void Expression(ActParam *actParam); - virtual void Term(ActParam *actParam); - virtual void Factor(ActParam *actParam); - virtual void MinDist(ActParam *actParam); - virtual void Range(ActParam *actParam); - virtual void PpemRange(ActParam *actParam); - - /***** Common *****/ - /*virtual void Error(short kind, short num);*/ - virtual void ErrorMsg(short kind, const wchar_t errMsg[]); -}; - -typedef struct { - wchar_t name[idLen]; - short minMatch; - FormParam param[maxFPs]; -} CommandDesc; - -CommandDesc tmtCmd[idents] = { - {L"Align", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}}}, - {L"ASM", 2, {{anyS, mand}}}, - {L"AutoHinter", 2, {{anyS, mand}}}, - {L"BeginCodePath", 1, {{anyN, mand}}}, - {L"Call", 2, {{anyN, mand}, {anyN, optR}}}, - {L"CompilerLogic", 2, {{compLogicN, mand}}}, - {L"CvtAllocation", 4, {{anyS, mand}}}, - {L"CvtLogic", 4, {{cvtLogicN, mand}}}, - {L"DAlign", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}, {ppemSize, opt}}}, - {L"DiagEndCtrl", 5, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {anyN, mand}}}, - {L"Diagonal", 5, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, - {L"DiagSerifs", 5, {{knotN, mand}, {knotN, mand}}}, - {L"DStroke", 2, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}}}, - {L"EndCodePath", 1}, - {L"FixDStrokes", 1}, - {L"GlyphStrokeAngle", 2, {{cvtN, mand}, {cvtN, mand}}}, - {L"GrabHereInX", 2, {{knotN, mand}, {knotN, mand}}}, - {L"Height", 1, {{knotN, mand}, {cvtN, mand}}}, - {L"Inline", 3, {{anyS, mand}}}, - {L"Intersect", 3, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {ppemSize, opt}, {ppemSize, opt}}}, - {L"IStroke", 2, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {cvtN, mand}, {phaseN, mand}, {cvtN, opt}}}, - {L"MainStrokeAngle", 1, {{angle100N, mand}}}, - {L"ProcessXSymmetrically", 8}, - {L"ProcessYSymmetrically", 8}, - {L"Quit", 1}, - - {L"ResIIPDDist", 8, {{knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // for the time being, we'll assume that parents and children have been constrained (moved) in y... - {L"ResIIPDLink", 8, {{knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN, mand}}}, // ... before calling ResIIPDDist/Link, hence we don't allow fv overrides - - {L"ResXAnchor", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {cvtN, opt}}}, // see also TMTSourceParser::Dispatch wherein the mandatory knot is considered a child point hence the ttvOpt override designates an alternate fv (unless angleFlag is used) - {L"ResXDDist", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResXDist", 6, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistFlagOnly, opt}}}, - {L"ResXDLink", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResXIPAnchor", 7, {{angleFlag, opt}, {postRoundFlag, opt}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // for the time being, we only allow one child, hence last parameter is a parent, which does not have to override the pv again - {L"ResXIPDDDist", 9, {{angleFlag, opt}, {knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResXIPDDist", 9, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, // for lc 'm' could use a TDist version, so think about generalizing to a MDist version - {L"ResXIPDDLink", 9, {{angleFlag, opt}, {knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResXIPDist", 8, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // currently we don't allow to override the fv of the parent (!) nor the child... - {L"ResXIPDLink", 8, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}}}, // for lc 'm' could use a TLink version, so think about generalizing to a MLink version - {L"ResXIPLink", 7, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN, mand}}}, // ...generalizing this could become a parameter-passing nightmare - {L"ResXLink", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, mand}, {minDistFlagOnly, opt}}}, - - {L"ResYAnchor", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {cvtN, opt}}}, // see also TMTSourceParser::Dispatch wherein the mandatory knot is considered a child point hence the ttvOpt override designates an alternate fv (unless angleFlag is used) - {L"ResYDDist", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResYDist", 6, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistFlagOnly, opt}}}, - {L"ResYDLink", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResYIPAnchor", 7, {{angleFlag, opt}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // for the time being, we only allow one child, hence last parameter is a parent, which does not have to override the pv again - {L"ResYIPDDDist", 9, {{knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResYIPDDist", 9, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, // for lc 'm' could use a TDist version, so think about generalizing to a MDist version - {L"ResYIPDDLink", 9, {{knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though - {L"ResYIPDist", 8, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // currently we don't allow to override the fv of the parent (!) nor the child... - {L"ResYIPDLink", 8, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}}}, // for lc 'm' could use a TLink version, so think about generalizing to a MLink version - {L"ResYIPLink", 7, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN, mand}}}, // ...generalizing this could become a parameter-passing nightmare - {L"ResYLink", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, mand}, {minDistFlagOnly, opt}}}, - - {L"Scoop", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}}}, - {L"Serif", 3, {{dirFlag, mand}, {serifN, mand}, {knotN, mand}}}, // context dependent number of knots adjusted on-the-fly in AdjustFPs - {L"SetItalicStrokeAngle", 16}, - {L"SetItalicStrokePhase", 16}, - {L"Smooth", 2, {{angleFlag, opt}}}, // !COM {L"Smooth", 2}, - {L"Stroke", 2, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, - {L"Tail", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, - {L"TweakMetrics", 2}, - {L"VacuFormLimit", 9, {{ppemSize, mand}}}, - {L"VacuFormRound", 9, {{dirFlag, mand}, {dirFlag, mand}, {curveN, mand}, {radiusN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, - - {L"XAlign", 3, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}, {ppemSize, opt}}}, - {L"XAnchor", 3, {{angleFlag, opt}, {knotNttvOpt, mand}}}, - {L"XBDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, - {L"XCDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, - {L"XDelta", 3, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, - {L"XDiagonal", 4, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, - {L"XDist", 4, {{angleFlag, opt}, {postRoundFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistGeneral, opt}}}, - {L"XDoubleGrid", 4, {{knotN, mand}, {knotN, optR}}}, - {L"XDownToGrid", 4, {{knotN, mand}, {knotN, optR}}}, - {L"XGDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, - {L"XHalfGrid", 2, {{knotN, mand}, {knotN, optR}}}, - {L"XInterpolate", 3, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... - {L"XInterpolate0", 13}, - {L"XInterpolate1", 13}, - {L"XIPAnchor", 4, {{angleFlag, opt}, {postRoundFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... - {L"XLink", 2, {{angleFlag, opt}, {postRoundFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, opt}, {minDistGeneral, opt}}}, - {L"XMove", 2, {{rationalN, mand}, {knotN, mand}, {knotN, optR}}}, - {L"XNoRound", 2, {{knotN, mand}, {knotN, optR}}}, - {L"XRound", 2, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, - {L"XShift", 3, {{angleFlag, opt}, {knotN, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // shift never respects the pv - {L"XSmooth", 3, {{angleFlag, opt}}}, // !COM {L"XSmooth", 3}, - {L"XStem", 4, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, - {L"XStroke", 4, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}}}, - {L"XUpToGrid", 2, {{knotN, mand}, {knotN, optR}}}, - - {L"YAlign", 3, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}, {ppemSize, opt}}}, - {L"YAnchor", 3, {{angleFlag, opt}, {knotNttvOpt, mand}, {cvtN, opt}}}, - {L"YBDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, - {L"YCDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, // ADD GREGH - {L"YDelta", 3, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, - {L"YDiagonal", 4, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, - {L"YDist", 4, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistGeneral, opt},}}, - {L"YDoubleGrid", 4, {{knotN, mand}, {knotN, optR}}}, - {L"YDownToGrid", 4, {{knotN, mand}, {knotN, optR}}}, - {L"YGDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, - {L"YHalfGrid", 2, {{knotN, mand}, {knotN, optR}}}, - {L"YInterpolate", 3, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... - {L"YInterpolate0", 13}, - {L"YInterpolate1", 13}, - {L"YIPAnchor", 4, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... - {L"YLink", 2, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, opt}, {minDistGeneral, opt}}}, - {L"YMove", 2, {{rationalN, mand}, {knotN, mand}, {knotN, optR}}}, - {L"YNoRound", 2, {{knotN, mand}, {knotN, optR}}}, - {L"YRound", 2, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, - {L"YShift", 3, {{angleFlag, opt}, {knotN, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // shift never respects the pv - {L"YSmooth", 3, {{angleFlag, opt}}}, // !COM {L"YSmooth", 3}, - {L"YStem", 4, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, - {L"YStroke", 4, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}}}, - {L"YUpToGrid", 2, {{knotN, mand}, {knotN, optR}}}, -}; - -FormParam minDistParam[maxFPs] = {{posRationalN, mand}, {ppemSize, mand}, {posRationalN, mand}}; // auxiliary parameter lists -FormParam rangeOfPpemNParam[maxFPs] = {{rationalN, mand}, {ppemN, mand}, {ppemN, mand}}; // for recursively calling MatchParameter -FormParam deltaColorParam[maxFPs] = {{colorN, mand}}; -FormParam knotNttvOptParams[maxFPs] = {{knotN, mand}, {knotN, mand}, {knotN, mand}}; - -void AdjustFPs(short serifType, FormParam *formParams); -void AdjustFPs(short serifType, FormParam *formParams) { - /* SERIF < (type, knot0, knot1, ... knotn-1) - param 0 1, 2, 3, param-1 */ - short params = 0,i; - - switch (serifType) { - case 0: params = 9; break; - case 1: - case 2: - case 3: params = 6; break; - case 4: params = 8; break; - } - for (i = 3; i < params; i++) formParams[i] = formParams[2]; - for (i = params; i < maxFPs-1; i++) formParams[i] = formParams[maxFPs-1]; -} /* AdjustFPs */ - -void TMTSourceParser::Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]) { - Symbol cmd; - ActParam aParam; - short formParamNum; - FormParam formParams[maxFPs]; - int32_t cmdStart,actParam; - - this->GetSym(); - while (this->errPos < 0 && Command(this)) { - cmdStart = this->prevPos; - cmd = this->sym; - if (cmd == invalid) this->ErrorMsg(syntactical,L"unknown VTT Talk command"); - if (cmd == xAnchor || cmd == xInterpolate || cmd == xIPAnchor || cmd == xLink || - cmd == yAnchor || cmd == yInterpolate || cmd == yIPAnchor || cmd == yLink) this->XFormToNewSyntax(); - this->GetSym(); - /***** - if (cmd == ::mainStrokeAngle || cmd == ::glyphStrokeAngle || cmd == setItalicStrokeAngle || cmd == setItalicStrokePhase) { - if (cmd == ::mainStrokeAngle && this->mainStrokeAngle || cmd == ::glyphStrokeAngle && this->glyphStrokeAngle || cmd == setItalicStrokeAngle && this->italicStrokeAngle || cmd == setItalicStrokePhase && this->italicStrokePhase) { - swprintf(errMsg,WIDE_STR_FORMAT L" already used in this glyph",tmtCmd[cmd].name); this->ErrorMsg(contextual,errMsg); - } - if (cmd == ::mainStrokeAngle && this->glyphStrokeAngle || cmd == ::glyphStrokeAngle && this->mainStrokeAngle) { - swprintf(errMsg,L"Cannot use both " WIDE_STR_FORMAT L" and " WIDE_STR_FORMAT L" in the same glyph",tmtCmd[::mainStrokeAngle].name,tmtCmd[::glyphStrokeAngle].name); this->ErrorMsg(contextual,errMsg); - } - if (cmd == ::mainStrokeAngle) this->mainStrokeAngle = true; - else if (cmd == ::glyphStrokeAngle) this->glyphStrokeAngle = true; - else if (cmd == setItalicStrokeAngle) this->italicStrokeAngle = true; - else if (cmd == setItalicStrokePhase) this->italicStrokePhase = true; - } - *****/ - // formParamNum = 0; formParams = &tmtCmd[cmd].param[0]; - for (formParamNum = 0; formParamNum < maxFPs; formParams[formParamNum] = tmtCmd[cmd].param[formParamNum], formParamNum++); - formParamNum = 0; - this->actParams = 0; - while (InitFlag(this)) { - this->paramPos[this->actParams] = this->prevPrevPos; - this->Flag(&aParam); this->MatchParameter(formParams,&formParamNum,&aParam.type); this->ValidateParameter(&aParam); - if (this->actParams < maxParams) - this->actParam[this->actParams++] = aParam; - else { - swprintf(errMsg,L"too many actual parameters (cannot have more than %li parameters)",maxParams); - this->ErrorMsg(contextual,errMsg); - } - } - if (this->sym == leftParen) this->GetSym(); else this->ErrorMsg(syntactical,L"( expected"); - if (InitParam(this)) { - this->paramPos[this->actParams] = this->prevPrevPos; - this->Parameter(&aParam); - this->MatchParameter(formParams,&formParamNum,&aParam.type); - this->ValidateParameter(&aParam); - if (this->actParams < maxParams) - this->actParam[this->actParams++] = aParam; - else { - swprintf(errMsg,L"too many actual parameters (cannot have more than %li parameters)",maxParams); - this->ErrorMsg(contextual,errMsg); - } - if (cmd == serif) AdjustFPs((short)(aParam.numValue/one6),formParams); // make the remaining formal parameters compatible with the actual serif type... - while (Separator(this) || InitParam(this)) { - if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); - if (Separator(this)) this->GetSym(); - this->paramPos[this->actParams] = this->prevPrevPos; - this->Parameter(&aParam); - if (!this->legacyCompile && InterpolateCmd(cmd) && !Separator(this) && !InitParam(this)) formParams[formParamNum].type = knotN; // drop ttvOpt of other parent - this->MatchParameter(formParams,&formParamNum,&aParam.type); - this->ValidateParameter(&aParam); - if (this->actParams < maxParams) - this->actParam[this->actParams++] = aParam; - else { - swprintf(errMsg,L"too many actual parameters (cannot have more than %li parameters)",maxParams); - this->ErrorMsg(contextual,errMsg); - } - } - } - while (formParamNum < maxFPs && opt <= formParams[formParamNum].pres && formParams[formParamNum].pres <= optR) formParamNum++; - if (formParamNum < maxFPs && formParams[formParamNum].type != voidParam) this->ErrorMsg(contextual,L"not enough parameters"); - if (this->sym == rightParen) this->GetSym(); else this->ErrorMsg(syntactical,L") expected"); - for (actParam = this->actParams; actParam < maxParams; actParam++) - this->paramPos[actParam] = this->prevPrevPos; - if (this->generators > 0 && this->errPos < 0) { - this->prevPrevPos = cmdStart; - this->Dispatch(cmd,this->actParams,this->actParam,errMsg); - if (errMsg[0]) this->ErrorMsg(contextual,errMsg); - } - if (cmd == quit) this->ErrorMsg(syntactical,L""); // generate dummy error to drop out of loop the same way as errors - } - if (this->sym != eot) this->ErrorMsg(syntactical,L"VTT Talk command expected"); - *changedSrc = this->changedSrc; - *errPos = this->errPos; - *errLen = this->symLen; - STRCPYW(errMsg,this->errMsg); -} /* TMTSourceParser::Parse */ - -#if _DEBUG -void TMTSourceParser::RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[]) { - int32_t beginPos,endPos; - - beginPos = endPos = -1; *changedSrc = false; - this->GetSym(); - while (this->sym != eot && this->errPos < 0 && !(beginPos >= 0 && endPos >= 0)) { - if (beginPos < 0 && this->sym == beginCodePath) { - this->GetSym(); this->GetSym(); this->GetSym(); this->SkipWhiteSpace(false); - beginPos = this->pos - chLookAhead; - } - if (endPos < 0 && this->sym == endCodePath) { - endPos = this->prevPos; - } - this->GetSym(); - } - if (beginPos >= 0 && endPos >= 0) { - this->talkText->Delete(endPos,this->talkText->TheLength()); - this->talkText->Delete(0,beginPos); - *changedSrc = true; - } - *errPos = this->errPos; - *errLen = this->symLen; -} // TMTSourceParser::RemoveAltCodePath -#endif - - -TMTSourceParser::TMTSourceParser(void) { - this->tanStraightAngle = tan(Rad(STRAIGHTANGLEFUDGE)); - this->italicStrokePhase = this->italicStrokeAngle = this->mainStrokeAngle = this->glyphStrokeAngle = false; - this->heights = new LinearListStruct; - this->partners = new LinearListStruct; - this->legacyCompile = false; -} // TMTSourceParser::TMTSourceParser - -TMTSourceParser::~TMTSourceParser(void) { - if (this->partners) delete this->partners; - if (this->heights) delete this->heights; -} // TMTSourceParser::~TMTSourceParser - -Height *TMTSourceParser::TheHeight(short at) { - Height *height; - - height = NULL; - if (this->heights) - for (height = (Height*)this->heights->first; height && height->of != at; height = (Height*)height->next); - return height; -} // TMTSourceParser::TheHeight - -void TMTSourceParser::RegisterHeight(short at, short cvt) { - Height *height; - bool found; - - height = this->TheHeight(at); - found = height != NULL; - if (!found) height = new Height; // else overwrite like GUI would - if (height) { - height->of = at; - height->cvtOverride = cvt; - if (!found) this->heights->InsertAtEnd(height); - } -} // TMTSourceParser::RegisterHeight - -Partner *TMTSourceParser::ThePartner(bool y, short from, short to) { - Partner *partner; - - partner = NULL; - if (this->partners) - for (partner = (Partner*)this->partners->first; partner && !((partner->of == from && partner->with == to) || (partner->of == to && partner->with == from)); partner = (Partner*)partner->next); - return partner; -} // TMTSourceParser::ThePartner - -void TMTSourceParser::RegisterPartner(short from, short to, bool y, bool round, short cvt) { - Partner *partner; - bool found; - - partner = this->ThePartner(y,from,to); - found = partner != NULL; - if (!found) partner = new Partner; // else overwrite like GUI would - if (partner) { - partner->direction = y ? linkY : linkX; - partner->category = round ? cvtRound : cvtStroke; - partner->of = from; partner->with = to; - partner->cvtOverride = cvt; - if (!found) this->partners->InsertAtEnd(partner); - } -} // TMTSourceParser::RegisterPartner - -void TMTSourceParser::InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]) { - short i; - - this->errPos = -1; this->symLen = 0; - this->errMsg[0] = L'\0'; - this->talkText = talkText; - this->font = font; - this->glyph = glyph; - this->knots = glyph->numContoursInGlyph > 0 ? glyph->endPoint[glyph->numContoursInGlyph-1] + 1 : 0; - this->knots += PHANTOMPOINTS; - this->generators = generators; - for (i = 0; i < generators; i++) this->gen[i] = gen[i]; - this->changedSrc = false; - this->pos = this->prevPos = this->prevPrevPos = 0; - this->ch2 = 0; // silence BC - this->legacyCompile = legacyCompile; - this->GetCh(); - this->GetCh(); -} /* TMTSourceParser::InitTMTParser */ - -void TMTSourceParser::TermTMTParser(void) { - -} /* TMTSourceParser::TermTMTParser */ - -bool TMTSourceParser::MakeProjFreeVector(bool haveFlag, int32_t flagValue, bool y, ActParam *parent, ActParam child[], int32_t children, ProjFreeVector *projFreeVector, wchar_t errMsg[]) { - TTVDirection flagToDir[numTTVDirections] = {xRomanDir, yRomanDir, xItalDir, yItalDir, xAdjItalDir, yAdjItalDir, diagDir, perpDiagDir}; - int32_t i,idx = 2*(haveFlag ? 1 + flagValue : 0) + (int32_t)y; - bool pvOverrideError = false,fvOverrideError = false; - - projFreeVector->pv.dir = flagToDir[idx%numTTVDirections]; - projFreeVector->pv.from = projFreeVector->pv.to = invalidKnotNum; - for (i = 0; i < children; i++) projFreeVector->fv[i] = projFreeVector->pv; - if (!this->legacyCompile) - { - - if (parent != NULL && parent->hasTtvOverride) { // NULL for Anchors, they have no parents - projFreeVector->pv = parent->ttvOverride; - if (projFreeVector->pv.from == invalidKnotNum) projFreeVector->pv.from = (short) (parent->numValue / one6); - if (!y && haveFlag) pvOverrideError = true; - } - for (i = 0; i < children && !pvOverrideError && !fvOverrideError; i++) { - if (child[i].hasTtvOverride) { - projFreeVector->fv[i] = child[i].ttvOverride; - if (projFreeVector->fv[i].from == invalidKnotNum) projFreeVector->fv[i].from = (short) (child[i].numValue / one6); - if (y && haveFlag) fvOverrideError = true; - } - } - if (pvOverrideError || fvOverrideError) - swprintf(errMsg, L"cannot override " WIDE_STR_FORMAT L" direction when using the italic or adjusted italic angle / or //", pvOverrideError ? L"projection" : L"freedom"); - } - return !(pvOverrideError || fvOverrideError); -} // TMTSourceParser::MakeProjFreeVector - -void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar_t errMsg[]) { - short i,j; - - errMsg[0] = L'\0'; - switch (cmd) { - case align: - case xAlign: - case yAlign: - case dAlign: { - short ppem = param[params-1].type == ppemSize ? (short)(param[params-1].numValue/one6) : -1, - children = params - (2 + (ppem >= 0)), // 2 parents... - parent0 = (short)(param[0].numValue/one6),parent1 = (short)(param[children+1].numValue/one6),child[maxParams]; - FVOverride fvOverride = cmd == align ? fvOldMethod : (cmd == xAlign ? fvForceX : (cmd == yAlign ? fvForceY : fvStandard)); - - for (i = 0; i < children; i++) child[i] = (short)(param[1+i].numValue/one6); - for (i = 0; i < this->generators; i++) this->gen[i]->Align(fvOverride,parent0,children,child,parent1,ppem,errMsg); - break; - } - case asM: - case InLine: - for (i = 0; i < this->generators; i++) this->gen[i]->Asm(cmd == InLine,param[0].litValue,errMsg); - break; - case autoHinter: - break; - case call: { - short anyNum[maxParams]; - - for (i = 0; i < params; i++) anyNum[i] = (short)(param[i].numValue/one6); - for (i = 0; i < this->generators; i++) this->gen[i]->Call(params-1,anyNum,anyNum[params-1]); - break; - } - case dStroke: { - bool leftStationary[2]; - short cvt = (short)((params <= 6) ? invalidCvtNum : param[6].numValue/one6); - short actualCvt; - short knot[4]; - wchar_t buf[32]; - - for (i = 0; i < 2; i++) leftStationary[i] = param[i].numValue > 0; - for (i = 0; i < 4; i++) knot[i] = (short)(param[2+i].numValue/one6); - for (i = 0; i < this->generators; i++) { - this->gen[i]->DStroke(leftStationary,knot,cvt,&actualCvt,errMsg); - if (actualCvt != cvt) { - swprintf(buf,L",%hi",actualCvt); this->Insert(this->paramPos[6],buf); - } - } - break; - } - case fixDStrokes: - for (i = 0; i < this->generators; i++) this->gen[i]->FixDStrokes(); - break; - case grabHereInX: - for (i = 0; i < this->generators; i++) this->gen[i]->GrabHereInX((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),errMsg); - break; - case intersect: { - short ppem0 = params > 5 ? (short)(param[5].numValue/one6) : noPpemLimit,ppem1 = params > 6 ? (short)(param[6].numValue/one6) : noPpemLimit; - - for (i = 0; i < this->generators; i++) this->gen[i]->Intersect((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),(short)(param[2].numValue/one6),(short)(param[3].numValue/one6),(short)(param[4].numValue/one6),ppem0,ppem1,errMsg); - break; - } - case iStroke: { - bool leftStationary[2]; - short knot[4],height[2],phase,actualCvt; - short cvt = (short)((params <= 9) ? invalidCvtNum : param[9].numValue/one6); - wchar_t buf[32]; - - for (i = 0; i < 2; i++) leftStationary[i] = param[i].numValue > 0; - for (i = 0; i < 4; i++) knot[i] = (short)(param[2+i].numValue/one6); - for (i = 0; i < 2; i++) height[i] = (short)(param[6+i].numValue/one6); - phase = (short)(param[8].numValue/one6); - for (i = 0; i < this->generators; i++) { - this->gen[i]->IStroke(leftStationary,knot,height,phase,cvt,&actualCvt,errMsg); - if (actualCvt != cvt) { - swprintf(buf,L",%hi",actualCvt); this->Insert(this->paramPos[9],buf); - } - } - break; - } - case ::mainStrokeAngle: - for (i = 0; i < this->generators; i++) this->gen[i]->MainStrokeAngle((short)(param[0].numValue/one6),errMsg); - break; - case ::glyphStrokeAngle: - for (i = 0; i < this->generators; i++) this->gen[i]->GlyphStrokeAngle((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),errMsg); - break; - case quit: - for (i = 0; i < this->generators; i++) this->gen[i]->Quit(); - break; - case scoop: - for (i = 0; i < this->generators; i++) this->gen[i]->Scoop((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),(short)(param[2].numValue/one6),errMsg); - break; - case serif: { - bool forward = param[0].numValue > 0; - short type = (short)(param[1].numValue/one6),knots,knot[7]; - - knots = params-2; // direction flag and type - for (i = 0; i < knots; i++) knot[i] = (short)(param[2+i].numValue/one6); - for (i = 0; i < this->generators; i++) this->gen[i]->Serif(forward,type,knots,knot,errMsg); - break; - } - case setItalicStrokeAngle: - for (i = 0; i < this->generators; i++) this->gen[i]->SetItalicStroke(false,errMsg); - break; - case setItalicStrokePhase: - for (i = 0; i < this->generators; i++) this->gen[i]->SetItalicStroke(true,errMsg); - break; - case smooth: - case xSmooth: - case ySmooth: { - short y = cmd == ySmooth ? 1 : (cmd == xSmooth ? 0 : -1); - short italicFlag = -1; - if (!this->legacyCompile) - { - bool haveFlag = params > 0 && param[0].type == angleFlag; - italicFlag = haveFlag ? (short) param[0].numValue : -1; - } - for (i = 0; i < this->generators; i++) this->gen[i]->Smooth(y,italicFlag); - break; - } - case diagonalMT: - case xDiagonal: - case yDiagonal: - case stroke: - case xStroke: - case yStroke: { - bool leftStationary[2],nearVert,nearHorz; - short base = cmd != xStroke && cmd != yStroke ? 2 : 0,optionalBase = base + 4, knot[4]; - short cvt = (short)((params <= optionalBase || param[optionalBase].type == ppemSize) ? invalidCvtNum : param[optionalBase].numValue/one6); - short ppem = (short)((params <= optionalBase || param[params-1].type != ppemSize) ? -1 : param[params-1].numValue/one6); - FVOverride fvOverride = cmd == stroke ? fvOldMethod : (cmd == xDiagonal || cmd == xStroke ? fvForceX : (cmd == yDiagonal || cmd == yStroke ? fvForceY : fvStandard)); - short actualCvt; - Vector leftEdge,rightEdge; - wchar_t buf[32]; - - for (i = 0; i < 4; i++) knot[i] = (short)((short)(param[base+i].numValue)/one6); - leftEdge.x = this->glyph->x[knot[2]] - this->glyph->x[knot[0]]; leftEdge.x = Abs(leftEdge.x); - leftEdge.y = this->glyph->y[knot[2]] - this->glyph->y[knot[0]]; leftEdge.y = Abs(leftEdge.y); - rightEdge.x = this->glyph->x[knot[3]] - this->glyph->x[knot[1]]; rightEdge.x = Abs(rightEdge.x); - rightEdge.y = this->glyph->y[knot[3]] - this->glyph->y[knot[1]]; rightEdge.y = Abs(rightEdge.y); - nearVert = leftEdge.x <= leftEdge.y*this->tanStraightAngle && rightEdge.x <= rightEdge.y*this->tanStraightAngle; - nearHorz = leftEdge.y <= leftEdge.x*this->tanStraightAngle && rightEdge.y <= rightEdge.x*this->tanStraightAngle; - if (cmd == xStroke && !nearVert) - swprintf(errMsg,L"cannot accept XSTROKE (edges differ from vertical axis by %f degrees or more)",(double)STRAIGHTANGLEFUDGE); - else if (cmd == yStroke && !nearHorz) - swprintf(errMsg,L"cannot accept YSTROKE (edges differ from horizontal axis by %f degrees or more)",(double)STRAIGHTANGLEFUDGE); - else if ((cmd == stroke || cmd == xStroke || cmd == yStroke) && (nearHorz || nearVert)) { // either nearHorz or nearVert, hence "informative" command - for (i = 0; i < 4; i += 2) for (j = 0; j < 4; j += 2) this->RegisterPartner(knot[i],knot[j+1],nearHorz,false,cvt); - } else { // diagonalMT, xDiagonal, yDiagonal, or general stroke "action" command - for (i = 0; i < 2; i++) leftStationary[i] = param[i].numValue > 0; - for (i = 0; i < this->generators; i++) { - this->gen[i]->Stroke(fvOverride,leftStationary,knot,cvt,ppem,&actualCvt,errMsg); - if (actualCvt != cvt) { - swprintf(buf,L",%hi",actualCvt); this->Insert(this->paramPos[6],buf); - } - } - } - break; - } - case xStem: - case yStem: - case xRound: - case yRound: { - short knot[2],cvt; - - cvt = params > 2 ? (short)param[2].numValue/one6 : invalidCvtNum; - for (i = 0; i < 2; i++) knot[i] = (short)(param[i].numValue/one6); - - this->RegisterPartner(knot[0],knot[1],cmd == yStem || cmd == yRound,cmd == xRound || cmd == yRound,cvt); - break; - } - case vacuFormLimit: - for (i = 0; i < this->generators; i++) this->gen[i]->VacuFormLimit((short)(param[0].numValue/one6)); - break; - case vacuFormRound: { - short knot[4]; - bool forward[2]; - - for (i = 0; i < 2; i++) forward[i] = param[i].numValue > 0; - for (i = 0; i < 4; i++) knot[i] = (short)(param[4+i].numValue/one6); - for (i = 0; i < this->generators; i++) this->gen[i]->VacuFormRound((short)(param[2].numValue/one6),(short)(param[3].numValue/one6),forward,knot,errMsg); - break; - } - case height: - this->RegisterHeight((short)(param[0].numValue/one6),(short)(param[1].numValue/one6)); - break; - case xAnchor: - case yAnchor: { - bool y = cmd == yAnchor,haveFlag = param[0].type == angleFlag; - ActParam *knotParam = ¶m[haveFlag]; - short knot = (short)(knotParam->numValue/one6); - short cvt = (short)((params <= haveFlag + 1) ? invalidCvtNum : param[haveFlag + 1].numValue/one6); - Height *height = y ? this->TheHeight(knot) : NULL; - short cvtHint = height ? height->cvtOverride : invalidCvtNum; - ProjFreeVector projFreeVector; - - if (cvt >= 0 && cvtHint >= 0) - swprintf(errMsg,L"cannot override a cvt number specified by a HEIGHT command"); - else { - if (cvt < 0) cvt = cvtHint; // no cvt override => try previously specified cvt - // since MDAP, MIAP don't ever use the dual projection vector, we'll define the knot to be a child, hence it gets to override the fv - // this may change once I implement overrides for italic and adjusted italic angles, and this may turn out to be somewhat tricky: - // if we measure perpendicular to the italic angle, and move in x, then subsequent instructions on the same knot must move along the italic angle, and measure in y - // in other words, XAnchor/(knot) overrides the pv, while YAnchor/(knot) overrides the fv. - if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,NULL,knotParam,1,&projFreeVector,errMsg)) { - for (i = 0; i < this->generators; i++) this->gen[i]->Anchor(cmd == yAnchor,&projFreeVector,knot,cvt,true,errMsg); - } - } - break; - } - case xLink: - case yLink: - case xDist: - case yDist: { - bool y = cmd == yLink || cmd == yDist, - dist = cmd == xDist || cmd == yDist, - haveFlag = param[0].type == angleFlag, - havePostRound = param[haveFlag].type == postRoundFlag; - short base = haveFlag+havePostRound+2; - bool haveCvt = params > base && param[base].type == cvtN, - haveMinDist = params > base+haveCvt && param[base+haveCvt].type == minDistGeneral,relative; - ActParam *parentParam = ¶m[haveFlag+havePostRound],*childParam = ¶m[haveFlag+havePostRound+1]; - short parent = (short)(parentParam->numValue/one6), - child = (short)(childParam->numValue/one6), - cvt = haveCvt ? (short)(param[base].numValue/one6) : invalidCvtNum,actualCvt = cvt, - minDists = haveMinDist ? param[base+haveCvt].minDists : -1, - jumpPpemSize[maxMinDist]; - int32_t *jSize = minDists >= 0 ? ¶m[base+haveCvt].jumpPpemSize[0] : NULL; - Partner *partner = this->ThePartner(y,parent,child); - CvtCategory cvtCategory = dist ? cvtAnyCategory : (partner ? partner->category : cvtDistance); - short cvtHint = partner ? partner->cvtOverride : invalidCvtNum; - short lsb = this->knots - PHANTOMPOINTS,rsb = lsb + 1; - CharGroup charGroup; - LinkColor linkColor; - LinkDirection linkDirection; - wchar_t buf[32]; - ProjFreeVector projFreeVector; - - if (cvt >= 0 && cvtHint >= 0) - swprintf(errMsg,L"cannot override a cvt number specified by an X|YSTROKE, an X|YSTEM, or an X|YROUND command"); - else if (dist && (cvtHint >= 0 || cvtCategory != cvtAnyCategory)) - swprintf(errMsg,L"cannot use a X|YDIST command preceeded by X|YSTROKE, X|YSTEM, or X|YROUND"); - else if (havePostRound && !haveFlag) - swprintf(errMsg,L"cannot use $ (post round flag) without using / (italic angle) or // (adjusted italic angle)"); - else { - if (!dist) { - if (cvt < 0) cvt = cvtHint; // no cvt override => try previously specified cvt - if (cvt >= 0) { - this->font->TheCvt()->GetCvtAttributes(cvt,&charGroup,&linkColor,&linkDirection,&cvtCategory,&relative); // fetch actual cvt category to encourage its use in GUI... - if (cvtCategory == cvtAnyCategory) cvtCategory = cvtDistance; // but default to cvtDistance if cvtAnyCategory to allow bi-directionality with GUI input of cvtAnyCategory as cvtDistance... - } else { - if (parent == lsb || child == lsb) cvtCategory = cvtLsb; - else if (parent == rsb || child == rsb) cvtCategory = cvtRsb; - // links lsb to rsb and v.v. not allowed by TTGenerator - } - } - - if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parentParam,childParam,1,&projFreeVector,errMsg)) { - for (i = 0; i < minDists; i++) jumpPpemSize[i] = (short)(jSize[i]/one6); - for (i = 0; i < this->generators; i++) { - this->gen[i]->Link(y,dist,&projFreeVector,havePostRound,parent,child,cvtCategory,cvt,minDists,jumpPpemSize,param[base+haveCvt].pixelSize,&actualCvt,errMsg); - if (actualCvt != cvt) { - swprintf(buf,L",%hi",actualCvt); this->Insert(this->paramPos[base],buf); - } - } - } - } - break; - } - case xMove: - case yMove: { - F26Dot6 amount = param[0].numValue; - short knots = params-1,knot[maxParams]; - - for (i = 0; i < knots; i++) knot[i] = (short)(param[1+i].numValue/one6); - for (i = 0; i < this->generators; i++) this->gen[i]->Move(cmd == yMove,amount,knots,knot,errMsg); - break; - } - case xBDelta: - case xCDelta: - case xDelta: - case xGDelta: - case yBDelta: - case yDelta: - case yCDelta: - case yGDelta: { - short knot = (short)(param[0].numValue/one6),j; - DeltaColor cmdColor = cmd == xDelta || cmd == yDelta ? alwaysDelta : (cmd == xBDelta || cmd == yBDelta ? blackDelta : (cmd == xGDelta || cmd == yGDelta ? greyDelta : ctNatVerRGBIAWBLYDelta)),paramColor; - - - for (j = 1; j < params; j++) { - paramColor = param[j].deltaColor; - if (cmdColor != alwaysDelta && paramColor != alwaysDelta){ - swprintf(errMsg,L"cannot override delta color specified by an X|YBDELTA or an X|YGDELTA or an X|YCDELTA command"); - }else{ - if (paramColor == alwaysDelta) paramColor = cmdColor; - for (i = 0; i < this->generators; i++) - this->gen[i]->Delta(cmd >= yBDelta,paramColor,knot,param[j].numValue,param[j].deltaPpemSize,errMsg); - } - } - break; - } - case xHalfGrid: - case yHalfGrid: - case xDoubleGrid: - case yDoubleGrid: - case xDownToGrid: - case yDownToGrid: - case xUpToGrid: - case yUpToGrid: - case xNoRound: - case yNoRound: { - short knot[maxParams]; - Rounding r = rnone; - - for (i = 0; i < params; i++) knot[i] = (short)(param[i].numValue/one6); - switch (cmd) { - case xHalfGrid: - case yHalfGrid: r = rthg; break; - case xDoubleGrid: - case yDoubleGrid: r = rtdg; break; - case xDownToGrid: - case yDownToGrid: r = rdtg; break; - case xUpToGrid: - case yUpToGrid: r = rutg; break; - case xNoRound: - case yNoRound: r = roff; break; - default: break; - } - for (i = 0; i < this->generators; i++) this->gen[i]->SetRounding(cmd == yHalfGrid || cmd == yDoubleGrid || cmd == yDownToGrid || cmd == yUpToGrid || cmd == yNoRound,r,params,knot); - break; - } - case xInterpolate: // xInterpolate0, xInterpolate1 mapped to xInterpolate - case yInterpolate: // yInterpolate0, yInterpolate1 mapped to yInterpolate - case xIPAnchor: - case yIPAnchor: { - bool y = cmd == yInterpolate || cmd == yIPAnchor, - haveFlag = param[0].type == angleFlag, - havePostRound = param[haveFlag].type == postRoundFlag; - ActParam *parent0Param = ¶m[haveFlag+havePostRound], *childParam = ¶m[haveFlag+havePostRound+1]; - short parent0 = (short)(parent0Param->numValue/one6),parent1 = (short)(param[params-1].numValue/one6),children,child[maxParams]; - ProjFreeVector projFreeVector; - - if (havePostRound && !haveFlag) - swprintf(errMsg,L"cannot use $ (post round flag) without using / (italic angle) or // (adjusted italic angle)"); - else { - children = params-2-havePostRound-haveFlag; // 2 parents... - if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parent0Param,childParam,children,&projFreeVector,errMsg)) { - for (i = 0; i < children; i++) child[i] = (short)(childParam[i].numValue/one6); - for (i = 0; i < this->generators; i++) this->gen[i]->Interpolate(y,&projFreeVector,havePostRound,parent0,children,child,parent1,cmd == xIPAnchor || cmd == yIPAnchor,errMsg); - } - } - break; - } - case xShift: - case yShift: { - bool y = cmd == yShift, - haveFlag = param[0].type == angleFlag; - ActParam *parentParam = ¶m[haveFlag], *childParam = ¶m[haveFlag+1]; - short parent = (short)(param[haveFlag].numValue/one6),children,child[maxParams]; - ProjFreeVector projFreeVector; - - children = params-1-haveFlag; - if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parentParam,childParam,children,&projFreeVector,errMsg)) { - for (i = 0; i < children; i++) child[i] = (short)(childParam[i].numValue/one6); - for (i = 0; i < this->generators; i++) this->gen[i]->Shift(y,&projFreeVector,parent,children,child,errMsg); - } - break; - } - // new rendering environment specific (Res) commands - case beginCodePath: - if (!this->legacyCompile) - { - short fpgmBias = (short)(param[0].numValue/one6); - - for (i = 0; i < this->generators; i++) this->gen[i]->BeginCodePath(fpgmBias,errMsg); - } - break; - case endCodePath: - if (!this->legacyCompile) - { - for (i = 0; i < this->generators; i++) this->gen[i]->EndCodePath(errMsg); - } - break; - case resXAnchor: - case resYAnchor: - if (!this->legacyCompile) - { - bool y = cmd == resYAnchor, - haveFlag = param[0].type == angleFlag; - short optParamOffs = haveFlag+1; - bool haveCvt = params > optParamOffs && param[optParamOffs].type == cvtN; - ActParam *childParam = ¶m[haveFlag]; - short child = (short)(childParam->numValue/one6), - cvt = haveCvt ? (short)(param[optParamOffs].numValue/one6) : invalidCvtNum; - ProjFreeVector projFreeVector; - - if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,NULL,childParam,1,&projFreeVector,errMsg)) { - for (i = 0; i < this->generators; i++) this->gen[i]->ResAnchor(cmd == resYAnchor,&projFreeVector,child,cvt,errMsg); - } - } - break; - case resXIPAnchor: - case resYIPAnchor: - if (!this->legacyCompile) - { - bool y = cmd == resYIPAnchor, - haveFlag = param[0].type == angleFlag, - havePostRound = param[haveFlag].type == postRoundFlag; - ActParam *parent0Param = ¶m[haveFlag+havePostRound], *childParam = ¶m[haveFlag+havePostRound+1]; - // ActParam *parent1Param = ¶m[haveFlag+havePostRound+2]; - short parent0 = (short)(parent0Param->numValue/one6),child = (short)(childParam->numValue/one6),parent1 = (short)(param[params-1].numValue/one6); - ProjFreeVector projFreeVector; - - if (havePostRound && !haveFlag) - swprintf(errMsg,L"cannot use $ (post round flag) without using / (italic angle) or // (adjusted italic angle)"); - else { - if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parent0Param,childParam,1,&projFreeVector,errMsg)) { - for (i = 0; i < this->generators; i++) this->gen[i]->ResIPAnchor(y,&projFreeVector,havePostRound,parent0,child,parent1,errMsg); - } - } - } - break; - case resXDist: - case resYDist: - case resXLink: - case resYLink: - if (!this->legacyCompile) - { - bool y = cmd == resYDist || cmd == resYLink, - dist = cmd == resXDist || cmd == resYDist, - haveFlag = param[0].type == angleFlag; - short optParamOffs = haveFlag+2; - bool haveCvt = params > optParamOffs && param[optParamOffs].type == cvtN, - haveMinDistFlag = params > optParamOffs+haveCvt && param[optParamOffs+haveCvt].type == minDistFlagOnly; - short minDists = haveMinDistFlag ? param[optParamOffs+haveCvt].minDists : -1; - ActParam *parentParam = ¶m[haveFlag],*childParam = ¶m[haveFlag+1]; - short parent = (short)(parentParam->numValue/one6), - child = (short)(childParam->numValue/one6), - cvt = haveCvt ? (short)(param[optParamOffs].numValue/one6) : invalidCvtNum; - ProjFreeVector projFreeVector; - - if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parentParam,childParam,1,&projFreeVector,errMsg)) { - for (i = 0; i < this->generators; i++) { - this->gen[i]->ResLink(y,dist,&projFreeVector,parent,child,cvt,minDists,errMsg); - } - } - } - break; - case resXIPDist: - case resYIPDist: - case resXIPLink: - case resYIPLink: - if (!this->legacyCompile) - { - bool y = cmd == resYIPDist || cmd == resYIPLink, - dist = cmd == resXIPDist || cmd == resYIPDist, - haveAngleFlag = param[0].type == angleFlag, - haveStrokeFlag = param[haveAngleFlag].type == strokeFlag; - ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], - *parentParam = ¶m[haveAngleFlag+haveStrokeFlag+1], - *childParam = ¶m[haveAngleFlag+haveStrokeFlag+2], - *cvtParam = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+3] : NULL, - *grandParent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+3]; - short strokeFlag = haveStrokeFlag ? (short)param[haveAngleFlag].numValue : 0, - grandParent0 = (short)(grandParent0Param->numValue/one6), - parent = (short)(parentParam->numValue/one6), - child = (short)(childParam->numValue/one6), - cvt = !dist ? (short)(cvtParam->numValue/one6) : invalidCvtNum, - grandParent1 = (short)(grandParent1Param->numValue/one6); - ProjFreeVector projFreeVector; - - if (this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,grandParent0Param,parentParam,2,&projFreeVector,errMsg)) { // 2 children (?) - for (i = 0; i < this->generators; i++) { - this->gen[i]->ResIPLink(y,dist,&projFreeVector,strokeFlag,grandParent0,parent,child,cvt,grandParent1,errMsg); - } - } - } - break; - case resXIPDDist: - case resYIPDDist: - case resXIPDLink: - case resYIPDLink: - if (!this->legacyCompile) - { - bool y = cmd == resYIPDDist || cmd == resYIPDLink, - dist = cmd == resXIPDDist || cmd == resYIPDDist, - haveAngleFlag = false, // so far, TT fn not implemented orthogonally enough - haveStrokeFlag = false; // so far, TT fn not implemented orthogonally enough - //short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, - // cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; - ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], - *parent0Param = ¶m[haveAngleFlag+haveStrokeFlag+1], - *child0Param = ¶m[haveAngleFlag+haveStrokeFlag+2], - *cvt0Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+3] : NULL, - *parent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+3], - *child1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+4], - *cvt1Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+!dist+5] : NULL, - *grandParent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+!dist+5]; - short strokeFlag = haveStrokeFlag ? (short)param[haveAngleFlag].numValue : 0, - grandParent0 = (short)(grandParent0Param->numValue/one6), - parent0 = (short)(parent0Param->numValue/one6), - child0 = (short)(child0Param->numValue/one6), - cvt0 = !dist ? (short)(cvt0Param->numValue/one6) : invalidCvtNum, - parent1 = (short)(parent1Param->numValue/one6), - child1 = (short)(child1Param->numValue/one6), - cvt1 = !dist ? (short)(cvt1Param->numValue/one6) : invalidCvtNum, - grandParent1 = (short)(grandParent1Param->numValue/one6); - ProjFreeVector projFreeVector; - - if (this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,grandParent0Param,parent0Param,4,&projFreeVector,errMsg)) { // 4 children (?) - for (i = 0; i < this->generators; i++) { - this->gen[i]->ResIPDLink(y,dist,&projFreeVector,strokeFlag,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1,errMsg); - } - } - } - break; - case resXIPDDDist: - case resYIPDDDist: - case resXIPDDLink: - case resYIPDDLink: - if (!this->legacyCompile) - { - bool y = cmd == resYIPDDDist || cmd == resYIPDDLink, - dist = cmd == resXIPDDDist || cmd == resYIPDDDist, - haveAngleFlag = param[0].type == angleFlag, - haveStrokeFlag = false; // so far, TT fn not implemented orthogonally enough - // short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, - // cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; - ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], - *parent0Param = ¶m[haveAngleFlag+haveStrokeFlag+1], - *child0Param = ¶m[haveAngleFlag+haveStrokeFlag+2], - *cvt0Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+3] : NULL, - *parent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+3], - *child1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+4], - *cvt1Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+!dist+5] : NULL, - *grandParent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+!dist+5]; - short strokeFlag = haveStrokeFlag ? (short)param[haveAngleFlag].numValue : 0, - grandParent0 = (short)(grandParent0Param->numValue/one6), - parent0 = (short)(parent0Param->numValue/one6), - child0 = (short)(child0Param->numValue/one6), - cvt0 = !dist ? (short)(cvt0Param->numValue/one6) : invalidCvtNum, - parent1 = (short)(parent1Param->numValue/one6), - child1 = (short)(child1Param->numValue/one6), - cvt1 = !dist ? (short)(cvt1Param->numValue/one6) : invalidCvtNum, - grandParent1 = (short)(grandParent1Param->numValue/one6); - ProjFreeVector projFreeVector0,projFreeVector1; - - if (this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,parent0Param,child0Param,1,&projFreeVector0,errMsg) && - this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,parent1Param,child1Param,1,&projFreeVector1,errMsg)) { - // pv not allowed to override, only fv, hence copy into single projFreeVector parameter - projFreeVector0.fv[1] = projFreeVector1.fv[0]; - for (i = 0; i < this->generators; i++) { - this->gen[i]->ResIPDDLink(y,dist,&projFreeVector0,strokeFlag,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1,errMsg); - } - } - } - break; - case resXDDist: - case resYDDist: - case resXDLink: - case resYDLink: - if (!this->legacyCompile) - { - bool y = cmd == resYDDist || cmd == resYDLink, - dist = cmd == resXDDist || cmd == resYDDist; - short cvtParamOffs0 = 2; - bool haveCvt0 = !dist && params > cvtParamOffs0 && param[cvtParamOffs0].type == cvtN; - short cvtParamOffs1 = 2 + haveCvt0 + 2; - bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN; - // bool haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; - ActParam *parent0Param = ¶m[0],*child0Param = ¶m[1],*parent1Param = ¶m[2+haveCvt0],*child1Param = ¶m[3+haveCvt0]; - short parent0 = (short)(parent0Param->numValue/one6), - child0 = (short)(child0Param->numValue/one6), - cvt0 = haveCvt0 ? (short)(param[cvtParamOffs0].numValue/one6) : invalidCvtNum, - parent1 = (short)(parent1Param->numValue/one6), - child1 = (short)(child1Param->numValue/one6), - cvt1 = haveCvt1 ? (short)(param[cvtParamOffs1].numValue/one6) : cvt0; - ProjFreeVector projFreeVector0,projFreeVector1; - - if (this->MakeProjFreeVector(false,0,y,parent0Param,child0Param,1,&projFreeVector0,errMsg) && - this->MakeProjFreeVector(false,0,y,parent1Param,child1Param,1,&projFreeVector1,errMsg)) { - // pv not allowed to override, only fv, hence copy into single projFreeVector parameter - projFreeVector0.fv[1] = projFreeVector1.fv[0]; - for (i = 0; i < this->generators; i++) { - this->gen[i]->ResDDLink(y,dist,&projFreeVector0,parent0,child0,cvt0,parent1,child1,cvt1,errMsg); - } - } - } - break; - case resIIPDDist: - case resIIPDLink: - if (!this->legacyCompile) - { - bool dist = cmd == resIIPDDist; - // short optParamOffs = cmd == resIIPDDist ? 6 : 8; - - short cvtParamOffs0 = 3; - bool haveCvt0 = !dist && params > cvtParamOffs0 && param[cvtParamOffs0].type == cvtN; - short cvtParamOffs1 = 3 + haveCvt0 + 2; - bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN; - // bool haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; - ActParam *grandParent0Param = ¶m[0],*parent0Param = ¶m[1],*child0Param = ¶m[2],*parent1Param = ¶m[3+haveCvt0],*child1Param = ¶m[4+haveCvt0],*grandParent1Param = ¶m[5+haveCvt0+haveCvt1]; - short grandParent0 = (short)(grandParent0Param->numValue/one6), - parent0 = (short)(parent0Param->numValue/one6), - child0 = (short)(child0Param->numValue/one6), - cvt0 = haveCvt0 ? (short)(param[cvtParamOffs0].numValue/one6) : invalidCvtNum, - parent1 = (short)(parent1Param->numValue/one6), - child1 = (short)(child1Param->numValue/one6), - grandParent1 = (short)(grandParent1Param->numValue/one6), - cvt1 = haveCvt1 ? (short)(param[cvtParamOffs1].numValue/one6) : cvt0; - ProjFreeVector projFreeVector0,projFreeVector1; - - if (this->MakeProjFreeVector(true,0,false,parent0Param,child0Param,1,&projFreeVector0,errMsg) && - this->MakeProjFreeVector(true,0,false,parent1Param,child1Param,1,&projFreeVector1,errMsg)) { - // pv not allowed to override, only fv, hence copy into single projFreeVector parameter - projFreeVector0.fv[1] = projFreeVector1.fv[0]; - for (i = 0; i < this->generators; i++) { - this->gen[i]->ResIIPDLink(dist,&projFreeVector0,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1,errMsg); - } - } - } - break; - case compilerLogic: - case cvtAllocation: - case cvtLogic: - case diagEndCtrl: - case diagSerifs: - case processXSymmetrically: - case processYSymmetrically: - case tail: - case tweakMetrics: - default: - swprintf(errMsg,L"Sorry, this command is no longer supported"); - break; - } -} /* TMTSourceParser::Dispatch */ - -void TMTSourceParser::XFormToNewSyntax(void) { -/* this is a bit of a botched job, but I'd rather have the standard parameter checking mechanism do all the serious work */ - int32_t savePos,flagPos,parmPos = 0; - wchar_t old[32],neu[64]; - short s,d,l; - - savePos = this->pos; - - while (this->ch && this->ch != L'(' && this->ch != L'[') this->GetCh(); // scan for start of parameter list - flagPos = this->pos-chLookAhead; - this->GetCh(); - while (this->ch && this->ch != L')' && this->ch != L']' && this->ch != L'"') { // find opening quote - if (this->ch == L',') parmPos = this->pos-chLookAhead; - this->GetCh(); - } - l = 0; - if (this->ch == L'"') { - this->GetCh(); - while (this->ch && this->ch != L'"' && l < 31) { old[l++] = this->ch; this->GetCh(); } // find closing quote - old[l] = '\0'; this->GetCh(); - } - - if (l > 0) { - this->talkText->Delete(parmPos,this->pos-chLookAhead); - - d = s = 0; - while (s < l) { - if (old[s] == L'/') { // italic angle - neu[d++] = old[s]; - old[s++] = L' '; - } else if (old[s] == 0xAF) { // adjusted italic angle - neu[d++] = L'/'; - neu[d++] = L'/'; - old[s++] = L' '; - } else if (old[s] == 0xA8) { // post round - neu[d++] = L'$'; - old[s++] = L' '; - } else { - s++; - } - } - if (d > 0) { - neu[d] = '\0'; - this->talkText->Insert(flagPos,neu); - parmPos += d; - } - - d = s = 0; - while (s < l-2) { - if ((old[s] == L'c' || old[s] == L'C') && (old[s+1] == L'v' || old[s+1] == L'V') && (old[s+2] == L't' || old[s+2] == L'T')) { // "cvt123" - neu[d++] = L','; - old[s++] = L' '; old[s++] = L' '; old[s++] = L' '; - while (s < l && ((L'0' <= old[s] && old[s] <= L'9') || old[s] == L'.' || old[s] == L' ')) { - if (old[s] != L' ') neu[d++] = old[s]; - old[s++] = L' '; - } - } else { - s++; - } - } - s = 0; - while (s < l) { - if (old[s] == L'<' || old[s] == 0xB3) { // "<" or "≥12" or "≥(12,@2,24)" - neu[d++] = L','; - if (old[s] == L'<') - neu[d++] = old[s]; - else { - neu[d++] = L'>'; - neu[d++] = L'='; - } - old[s++] = L' '; - if (s < l && old[s] == L'(') { // "≥(12,@2,24)" - neu[d++] = old[s]; old[s++] = L' '; // '(' - while (s < l-1 && old[s] != L',') { neu[d++] = old[s]; old[s++] = L' '; } - neu[d++] = old[s]; old[s++] = L' '; // ',' - neu[d++] = L'@'; - while (s < l-1 && old[s] != L')') { neu[d++] = old[s]; old[s++] = L' '; } - neu[d++] = old[s]; old[s++] = L' '; // ')' - } else { // "≥12" - while (s < l && ((L'0' <= old[s] && old[s] <= L'9') || old[s] == L'.')) { neu[d++] = old[s]; old[s++] = L' '; } - } - } else { - s++; - } - } - if (d > 0) { - neu[d] = '\0'; - this->talkText->Insert(parmPos,neu); - parmPos += d; - } - - d = s = 0; - neu[d++] = L','; - neu[d++] = L'"'; - while (s < l) { - if (old[s] != L' ') neu[d++] = old[s]; - s++; - } - neu[d++] = L'"'; - if (d > 3) { - neu[d] = '\0'; - this->talkText->Insert(parmPos,neu); - parmPos += d; - } - this->changedSrc = true; - } - - this->pos = savePos-chLookAhead; - this->GetCh(); this->GetCh(); -} /* TMTSourceParser::XFormToNewSyntax */ - -/*****/ -void TMTSourceParser::Flag(ActParam *actParam) { - int32_t paramStart; - - paramStart = this->prevPos; - switch (this->sym) { - case leftDir: - case rightDir: - actParam->type = dirFlag; actParam->numValue = (int32_t)this->sym - (int32_t)leftDir; - this->GetSym(); - break; - case italAngle: - case adjItalAngle: - actParam->type = angleFlag; actParam->numValue = (int32_t)this->sym - (int32_t)italAngle; - this->GetSym(); - break; - case optStroke: - case optStrokeLeftBias: - case optStrokeRightBias: - if (!this->legacyCompile) - { - actParam->type = strokeFlag; actParam->numValue = (int32_t)this->sym - (int32_t) optStroke + 1; - this->GetSym(); - } - break; - case postRound: - actParam->type = postRoundFlag; - this->GetSym(); - break; - default: - break; - } - this->prevPrevPos = paramStart; -} // TMTSourceParser::Flag - -/***** -void TMTSourceParser::Flag(ActParam *actParam) { - int32_t paramStart; -#ifdef VTT_PRO_SP_YAA_AUTO - bool doubleSlash; - ActParam fvPoint0,fvPoint1; - short subParams; -#endif - - paramStart = this->prevPos; - switch (this->sym) { - case leftDir: - case rightDir: - actParam->type = dirFlag; actParam->numValue = (int32_t)this->sym - (int32_t)leftDir; - this->GetSym(); - break; - case italAngle: - case adjItalAngle: -#ifdef VTT_PRO_SP_YAA_AUTO - doubleSlash = this->sym == adjItalAngle; -#endif - actParam->type = angleFlag; - actParam->numValue = (int32_t)this->sym - (int32_t)italAngle; - this->GetSym(); -#ifdef VTT_PRO_SP_YAA_AUTO - if (InitParam(this)) { - if (!doubleSlash) this->ErrorMsg(syntactical,L"Cannot use / to delimit freedom vector direction (use // instead)"); - subParams = 0; - this->Parameter(&fvPoint0); this->MatchParameter(fvPointsParam,&subParams,&fvPoint0.type); this->ValidateParameter(&fvPoint0); - if (Separator(this) || InitParam(this)) { - if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); - if (Separator(this)) this->GetSym(); - this->Parameter(&fvPoint1); this->MatchParameter(fvPointsParam,&subParams,&fvPoint1.type); this->ValidateParameter(&fvPoint1); - } else { - fvPoint1 = fvPoint0; - } - if (this->sym == adjItalAngle) this->GetSym(); else this->ErrorMsg(syntactical,L"// expected"); - actParam->fvPoint0 = (short)(fvPoint0.numValue/one6); - actParam->fvPoint1 = (short)(fvPoint1.numValue/one6); - } else { - actParam->fvPoint0 = actParam->fvPoint1 = invalidKnotNum; - } -#endif - break; - case postRound: - actParam->type = postRoundFlag; - this->GetSym(); - break; - } - this->prevPrevPos = paramStart; -} // TMTSourceParser::Flag -*****/ - -void TMTSourceParser::Parameter(ActParam *actParam) { - int32_t paramStart,localParamStart; - short subParams; - ActParam colorParam; - Symbol ttvSym; - ParamType paramType; - int32_t numValue,firstLocalParamStart = 0; - bool gotKnot[2]; - - paramStart = this->prevPos; - if (leftParen <= this->sym && this->sym <= rational) { - this->Expression(actParam); - this->prevPrevPos = paramStart; // can't recursively call this->Parameter() here... - actParam->hasTtvOverride = false; - actParam->ttvOverride.dir = xRomanDir; // say - actParam->ttvOverride.from = invalidKnotNum; - actParam->ttvOverride.to = invalidKnotNum; - - if (this->sym == aT) { - subParams = 0; - if (actParam->type == anyN) actParam->type = rationalN; // for now - this->MatchParameter(rangeOfPpemNParam,&subParams,&actParam->type); - this->ValidateParameter(actParam); - this->GetSym(); - this->PpemRange(actParam); - actParam->deltaColor = alwaysDelta; - if (this->sym == percent) { // optional delta color sub-parameter - this->GetSym(); - this->Parameter(&colorParam); - if (colorParam.type == anyN) colorParam.type = colorN; - subParams = 0; - this->MatchParameter(deltaColorParam,&subParams,&colorParam.type); - this->ValidateParameter(&colorParam); - actParam->deltaColor = DeltaColorOfByte((unsigned char)(colorParam.numValue/one6)); - actParam->type = rangeOfPpemNcolorOpt; // by now - } - } else if (!this->legacyCompile && (this->sym == colon || this->sym == rightDir || this->sym == upDir)) { - // the following are all valid: - // - // knot no ttv override - // knot > ttv in x-direction - // knot ^ ttv in y-direction - // knot > knot1 ttv on line from knot to knot1 - // knot ^ knot1 ttv perpendicular to line from knot to knot1 - // knot : knot0 > knot1 ttv on line from knot0 to knot1 - // knot : knot0 ^ knot1 ttv perpendicular to line from knot to knot1 - // - // (no italic or adjusted italic angle yet, - // this would probably complement the ^ and > by / or // ... - // or use \ and \\ for perpendicular to the italic angle and don't require ^ and > ???) - // - // at this point we have parsed knot, now we're about to parse the rest - - actParam->hasTtvOverride = true; - - subParams = 0; - paramType = actParam->type; - if (paramType == anyN) actParam->type = knotN; // for now - this->MatchParameter(knotNttvOptParams,&subParams,&actParam->type); this->ValidateParameter(actParam); - numValue = actParam->numValue; - - gotKnot[0] = false; - - if (this->sym == colon) { - this->GetSym(); - - firstLocalParamStart = localParamStart = this->prevPos; - this->Expression(actParam); - this->prevPrevPos = localParamStart; - this->MatchParameter(knotNttvOptParams,&subParams,&actParam->type); this->ValidateParameter(actParam); - if (actParam->type == knotN) actParam->ttvOverride.from = (short)(actParam->numValue/one6); - - gotKnot[0] = true; - } - - if (this->sym != rightDir && this->sym != upDir) - this->ErrorMsg(syntactical,L"> or ^ expected"); - else { - ttvSym = this->sym; - this->GetSym(); - } - - gotKnot[1] = false; - - if (this->sym != rightParen && !Separator(this)) { - - localParamStart = this->prevPos; - this->Expression(actParam); - this->prevPrevPos = localParamStart; - this->MatchParameter(knotNttvOptParams,&subParams,&actParam->type); this->ValidateParameter(actParam); - if (actParam->type == knotN) actParam->ttvOverride.to = (short)(actParam->numValue/one6); - - gotKnot[1] = true; - } - - if (gotKnot[0] && !gotKnot[1]) { - this->prevPrevPos = firstLocalParamStart; - this->ErrorMsg(contextual,L"invalid freedom or projection vector (second knot expected)"); - } else if (gotKnot[0] && gotKnot[1] && actParam->ttvOverride.from == actParam->ttvOverride.to) { - this->prevPrevPos = firstLocalParamStart; - this->ErrorMsg(contextual,L"invalid freedom or projection vector (knots must differ)"); - } - - // no italic or adjusted italic angle yet - actParam->ttvOverride.dir = gotKnot[1] ? (ttvSym == upDir ? perpDiagDir : diagDir) : (ttvSym == upDir ? yRomanDir : xRomanDir); - - if (paramType == anyN) actParam->type = gotKnot[0] || gotKnot[1] ? knotNttvOpt : knotNttvOptXY; // reset - actParam->numValue = numValue; - } - } else if (this->sym == aT) { - this->GetSym(); - localParamStart = this->prevPos; - this->Expression(actParam); - this->prevPrevPos = localParamStart; // can't recursively call this->Parameter() here... - if (actParam->type != anyN) { - this->ErrorMsg(contextual,L"ppem size expected (can be an integer only)"); - actParam->numValue = one6; - } - actParam->type = ppemSize; - } else if (this->sym == literal) { - actParam->type = anyS; actParam->litValue = this->litValue; - this->GetSym(); - } else if (this->sym == atLeast || this->sym == leftDir) { - this->MinDist(actParam); - } else { - this->ErrorMsg(syntactical,L"parameter starts with invalid symbol (+, -, @, <, >=, number, or \x22string\x22 expected)"); actParam->type = voidParam; actParam->numValue = 0; - } - this->prevPrevPos = paramStart; -} /* TMTSourceParser::Parameter */ - -bool Match(ParamType formParamType, ParamType actParamType); -bool Match(ParamType formParamType, ParamType actParamType) { - return (actParamType == formParamType || - (actParamType == anyN && anyN <= formParamType && formParamType <= posRationalN) || - // actParamType == knotNttvOpt && knotNttvOpt <= formParamType && formParamType <= k - (knotNttvOpt <= actParamType && actParamType <= knotNttvOptXY && knotNttvOpt <= formParamType && formParamType <= knotNttvOptXY) || - (actParamType == rangeOfPpemN && formParamType == rangeOfPpemNcolorOpt) || - (actParamType == posRationalN && rationalN <= formParamType && formParamType <= posRationalN) || - (actParamType == minDistFlagOnly && formParamType == minDistGeneral)); -} /* Match */ - -void TMTSourceParser::MatchParameter(FormParam *formParams, short *formParamNum, ParamType *actParamType) { - short tentative; - wchar_t errMsg[maxLineSize]; - ParamType expected; - - tentative = *formParamNum; - while (tentative < maxFPs && !Match(formParams[tentative].type,*actParamType) && opt <= formParams[tentative].pres && formParams[tentative].pres <= optR) tentative++; - if (*formParamNum < tentative && tentative < maxFPs && Match(formParams[tentative].type,*actParamType)) *formParamNum = tentative; // skip optional parameters - if (*formParamNum < maxFPs && formParams[*formParamNum].type != voidParam) { - if (Match(formParams[*formParamNum].type,*actParamType)) - *actParamType = formParams[*formParamNum].type; - else { - expected = *formParamNum < maxFPs ? formParams[*formParamNum].type : voidParam; - switch (expected) { - case anyN: - case knotN: - case knotNttvOpt: - case knotNttvOptXY: - case cvtN: - case compLogicN: - case cvtLogicN: - case phaseN: - case angle100N: - case colorN: - case serifN: - case curveN: - case radiusN: swprintf(errMsg,L"integer number expected (example: 1)"); break; - case rationalN: swprintf(errMsg,L"rational number expected (example: 1/8 or -1.5)"); break; - case posRationalN: swprintf(errMsg,L"positive rational number expected (example: 1/8 or 1.5)"); break; - case ppemSize: swprintf(errMsg,L"ppem size expected (example: @12)"); break; - case ppemN: swprintf(errMsg,L"ppem number expected (example: 12)"); break; - case rangeOfPpemNcolorOpt: - case rangeOfPpemN: swprintf(errMsg,L"ppem range expected (example: @8..13;21)"); break; - case anyS: swprintf(errMsg,L"quoted string expected (example: \x22V1.11\x22 or %cCALL[], 9\x22)",'\x22'); break; // %c or else compiler won't accept "escape sequence"... - case minDistFlagOnly: swprintf(errMsg,L"minimum distance flag expected (example: < or >= only)"); break; - case minDistGeneral: swprintf(errMsg,L"minimum distance expected (example: < or >= or >=1.5 or >=(1.5,@12,2.5) )"); break; - case dirFlag: swprintf(errMsg,L"direction flag expected (example: either < or >)"); break; - case angleFlag: swprintf(errMsg,L"angle flag expected (example: either / or //)"); break; - case postRoundFlag: swprintf(errMsg,L"post round flag expected (example: $)"); break; - default: swprintf(errMsg,L"actual parameter does not match"); break; - } - this->ErrorMsg(contextual,errMsg); - } - if (mand <= formParams[*formParamNum].pres && formParams[*formParamNum].pres <= opt) (*formParamNum)++; // use next parameter next time; - } else { - this->ErrorMsg(contextual,L"too many parameters"); - } -} /* TMTSourceParser::MatchParameter */ - -void TMTSourceParser::ValidateParameter(ActParam *actParam) { - /* test if actParam->type is in range of phaseN or angle100N or whatever it is by now */ - wchar_t errMsg[maxLineSize]; - - switch (actParam->type) { // which by now is the formal parameter type - case voidParam: - break; - case anyN: - break; - case knotNttvOpt: - case knotN: { - int32_t knot = actParam->numValue/one6; - - if (knot < 0 || knot >= this->knots) { - swprintf(errMsg,L"invalid knot number (can be in range 0 through %hi only)",this->knots-1); this->ErrorMsg(contextual,errMsg); - actParam->numValue = 0; - } - // for knotNttvOpt, ttvKnot[0], and ttvKnot[1] already validated against being in range - // what's left to do is to verify (if we can) whether this yields an almost perpendicular case. - // probably can't do at this level, because we don't know the value of the projection vector. - // would need to know tt->PVDir() but this may not be valid if compiling into graphical representation - break; - } - case cvtN: { - int32_t cvt = actParam->numValue/one6; - - if (!this->font->TheCvt()->CvtNumExists(cvt)) { - this->ErrorMsg(contextual,L"invalid cvt number (must be defined in the control value table)"); - actParam->numValue = 0; - } - break; - } - case compLogicN: - break; - case cvtLogicN: - break; - case phaseN: - if (actParam->numValue < 0 || actParam->numValue >= phases*one6) { - swprintf(errMsg,L"invalid phase type (can be in range 0 through %li only)",phases-1); this->ErrorMsg(contextual,errMsg); - actParam->numValue = 0; - } - break; - case angle100N: - if (actParam->numValue < 0 || actParam->numValue > maxAngle*one6) { - swprintf(errMsg,L"invalid angle x100 (can be in range 0 through %li only)",maxAngle); this->ErrorMsg(contextual,errMsg); - actParam->numValue = 0; - } - break; - case colorN: - if (DeltaColorOfByte((unsigned char)(actParam->numValue/one6)) == invalidDelta) { - swprintf(errMsg,L"invalid delta color flag (can be " NARROW_STR_FORMAT L" only)",AllDeltaColorBytes()); this->ErrorMsg(contextual,errMsg); - actParam->numValue = 0; - } - break; - case serifN: - if (actParam->numValue < 0 || actParam->numValue >= serifs*one6) { - swprintf(errMsg,L"invalid serif type (can be in range 0 through %li only)",serifs-1); this->ErrorMsg(contextual,errMsg); - actParam->numValue = 0; - } - break; - case curveN: - break; - case radiusN: - break; - case rationalN: - case posRationalN: - if ((actParam->type == posRationalN && actParam->numValue < 0) || actParam->numValue < -maxPixelValue || actParam->numValue > maxPixelValue) { - swprintf(errMsg,L"invalid pixel size (can be in range %li through %li only)",actParam->type == posRationalN ? 0 : -maxPixelValue/one6,maxPixelValue/one6); this->ErrorMsg(contextual,errMsg); - actParam->numValue = one6; - } - if (actParam->numValue == 0) { - this->ErrorMsg(contextual,L"pixel size cannot be 0"); - actParam->numValue = one6; - } - break; - case ppemSize: - case ppemN: - if (actParam->numValue < one6 || actParam->numValue >= maxPpemSize*one6) { - swprintf(errMsg,L"invalid ppem number (can be in range 1 through %li only)",maxPpemSize-1); this->ErrorMsg(contextual,errMsg); - actParam->numValue = one6; - } - break; - case rangeOfPpemN: - /* this->numValue already validated in PpemRange to be able to report errors at correct source position, likewise this->deltaPpemSize */ - break; - case anyS: - break; - case minDistGeneral: - /* this->jumpPpemSize[maxMinDist] and this->pixelSize[maxMinDist] already validated in MinDist for same reason as case deltaRangeN: */ - break; - case dirFlag: - break; - case angleFlag: - break; - case postRoundFlag: - break; - default: - break; - } -} /* TMTSourceParser::ValidateParameter */ - -void TMTSourceParser::Expression(ActParam *actParam) { - Symbol sign,op; - ActParam actParam2; - wchar_t errMsg[maxLineSize]; - - sign = plus; - if (this->sym == plus || this->sym == minus) { - sign = this->sym; this->GetSym(); - } - this->Term(actParam); - if (sign == minus) actParam->numValue = -actParam->numValue; - while (this->sym == plus || this->sym == minus) { - op = this->sym; this->GetSym(); - this->Term(&actParam2); - if (op == plus) actParam->numValue += actParam2.numValue; else actParam->numValue -= actParam2.numValue; // assuming we have not more than 32 - 17 - 1 binary places - if (Abs(actParam->numValue) >= (shortMax+1)*one6) { - if (op == plus) - swprintf(errMsg,L"result of addition too large (cannot be %li or above)",shortMax+1); - else - swprintf(errMsg,L"result of subtraction too large (cannot be -%li or below)",shortMax+1); - this->ErrorMsg(contextual,errMsg); - } - actParam->type = Max(actParam->type,actParam2.type); - } - if (actParam->type == rationalN && actParam->numValue >= 0) actParam->type = posRationalN; -} /* TMTSourceParser::Expression */ - -void TMTSourceParser::Term(ActParam *actParam) { - Symbol op; - ActParam actParam2; - wchar_t errMsg[maxLineSize]; - - this->Factor(actParam); - while (this->sym == timeS || this->sym == italAngle) { - op = this->sym; this->GetSym(); - this->Factor(&actParam2); - if (op == timeS) { - if ((double)Abs(actParam->numValue)*(double)Abs(actParam2.numValue) < (double)((shortMax+1)*one6*one6)) - actParam->numValue = (actParam->numValue*actParam2.numValue + half6)/one6; - else { - swprintf(errMsg,L"result of multiplication too large (cannot be %li or larger in magnitude)",shortMax+1); this->ErrorMsg(contextual,errMsg); - } - } else { // op == italAngle, i.e. divide - if (actParam2.numValue != 0 && (double)Abs(actParam->numValue) < (double)(shortMax+1)*(double)Abs(actParam2.numValue)) { - if (actParam->type == anyN && actParam2.type == anyN && actParam->numValue%actParam2.numValue != 0) actParam->type = rationalN; - actParam->numValue = (2*actParam->numValue*one6 + actParam2.numValue)/(2*actParam2.numValue); - } else { - swprintf(errMsg,L"result of division too large (cannot be %li or larger in magnitude)",shortMax+1); this->ErrorMsg(contextual,errMsg); - } - } - actParam->type = Max(actParam->type,actParam2.type); - } -} /* TMTSourceParser::Term */ - -void TMTSourceParser::Factor (ActParam *actParam) { - if (this->sym == natural || this->sym == rational) { - actParam->type = this->sym == natural ? anyN : rationalN; - actParam->numValue = this->numValue; this->GetSym(); - } else if (this->sym == leftParen) { - this->GetSym(); - this->Expression(actParam); - if (this->sym == rightParen) this->GetSym(); else this->ErrorMsg(syntactical,L") expected"); - } else { - this->ErrorMsg(syntactical,L"factor starts with invalid symbol (number or ( expected)"); - actParam->type = voidParam; - } -} /* TMTSourceParser::Factor */ - -void TMTSourceParser::MinDist(ActParam *actParam) { - Symbol op; - bool haveLeftParen,gotMinDist; - short subParams; - ActParam ppem,pixel; - wchar_t errMsg[maxLineSize]; - - op = this->sym; - actParam->type = minDistFlagOnly; - actParam->minDists = 0; - this->GetSym(); - if (op == atLeast) { - haveLeftParen = false; - if (this->sym == leftParen) { haveLeftParen = true; this->GetSym(); } - gotMinDist = InitParam(this); - subParams = 0; - if (gotMinDist) { this->Parameter(&pixel); this->MatchParameter(minDistParam,&subParams,&pixel.type); this->ValidateParameter(&pixel); } - else { pixel.type = anyN; pixel.numValue = one6; } - actParam->jumpPpemSize[actParam->minDists] = one6; // say... - actParam->pixelSize[actParam->minDists] = pixel.numValue; - actParam->minDists++; - if (haveLeftParen && gotMinDist) { - while (Separator(this) || InitParam(this)) { - if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); - if (Separator(this)) this->GetSym(); - this->Parameter(&ppem); this->MatchParameter(minDistParam,&subParams,&ppem.type); this->ValidateParameter(&ppem); - if (ppem.numValue <= actParam->jumpPpemSize[actParam->minDists-1]) { - this->ErrorMsg(contextual,L"this ppem size should be larger than the previous one"); - ppem.numValue = actParam->jumpPpemSize[actParam->minDists-1] + one6; - } - if (Separator(this) || InitParam(this)) { - if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); - if (Separator(this)) this->GetSym(); - this->Parameter(&pixel); this->MatchParameter(minDistParam,&subParams,&pixel.type); this->ValidateParameter(&pixel); - if (pixel.numValue <= actParam->pixelSize[actParam->minDists-1]) { - this->ErrorMsg(contextual,L"this pixel size should be larger than the previous one"); - pixel.numValue = actParam->pixelSize[actParam->minDists-1] + one6; - } - } else { - this->ErrorMsg(syntactical,L", (followed by another pixel size) expected"); - } - if (actParam->minDists < maxMinDist) { - actParam->jumpPpemSize[actParam->minDists] = ppem.numValue; - actParam->pixelSize[actParam->minDists] = pixel.numValue; - actParam->minDists++; - } else { - swprintf(errMsg,L"too many minimum distances (cannot have more than %li)",maxMinDist); this->ErrorMsg(contextual,errMsg); - } - } - } - if (haveLeftParen) { - if (this->sym == rightParen) this->GetSym(); else this->ErrorMsg(syntactical,L") expected"); - } - if (gotMinDist || actParam->minDists > 1 || actParam->pixelSize[0] != one6 || actParam->jumpPpemSize[0] != one6) { - actParam->type = minDistGeneral; - } - } -} /* TMTSourceParser::MinDist */ - -void TMTSourceParser::Range(ActParam *actParam) { - ActParam lowParam,highParam; - short subParams,low,high,i; - wchar_t errMsg[maxLineSize]; - - subParams = 1; // skip pixel number - this->Parameter(&lowParam); - if (lowParam.type == anyN) lowParam.type = ppemN; - this->MatchParameter(rangeOfPpemNParam,&subParams,&lowParam.type); - this->ValidateParameter(&lowParam); - low = high = (short)(lowParam.numValue/one6); - if (this->sym == ellipsis) { - this->GetSym(); - this->Parameter(&highParam); - if (highParam.type == anyN) highParam.type = ppemN; - this->MatchParameter(rangeOfPpemNParam,&subParams,&highParam.type); - this->ValidateParameter(&highParam); - high = (short)(highParam.numValue/one6); - if (low > high) { this->ErrorMsg(contextual,L"low end of ppem range cannot be above high end"); high = low; } - } - for (i = low; i <= high; i++) { - if (!actParam->deltaPpemSize[i]) - actParam->deltaPpemSize[i] = true; - else { - swprintf(errMsg,L"ppem size %hi occurs more than once",i); - this->ErrorMsg(contextual,errMsg); - } - } -} /* TMTSourceParser::Range */ - -void TMTSourceParser::PpemRange(ActParam *actParam) { - /* e.g. XDelta(13, 3/8 @ 12..18; 20; 24, 2/8 @ 19; 21..23) */ - short i; - - actParam->type = rangeOfPpemN; // by now - for (i = 0; i < maxPpemSize; i++) actParam->deltaPpemSize[i] = false; - this->Range(actParam); - while (this->sym == semiColon || InitParam(this)) { - if (this->sym == semiColon) this->GetSym(); else this->ErrorMsg(syntactical,L"; expected"); - this->Range(actParam); - } -} /* TMTSourceParser::PpemRange */ - -void TMTSourceParser::GetCh(void) { - this->ch = this->ch2; - this->ch2 = this->talkText->GetCh(this->pos); - this->pos++; -} /* TMTSourceParser::GetCh */ - -void TMTSourceParser::SkipComment(void) { - int32_t startPos; - - startPos = this->pos-chLookAhead; - this->GetCh(); this->GetCh(); - while (this->ch && !TermComment(this)) { - if (InitComment(this)) this->SkipComment(); else this->GetCh(); - } - if (this->ch) { - this->GetCh(); this->GetCh(); - } else { - this->prevPos = startPos; - this->ErrorMsg(special,L"comment opened but not closed"); - } -} /* TMTSourceParser::SkipComment */ - -void TMTSourceParser::SkipWhiteSpace(bool includingComments) { - while (WhiteSpace(this) || (includingComments && InitComment(this))) { - if (WhiteSpace(this)) this->GetCh(); - if (includingComments && InitComment(this)) this->SkipComment(); - } -} /* TMTSourceParser::Skip */ - -void TMTSourceParser::GetNumber(void) { - bool overflow; - int32_t digit,decPlcs,pwrOf10; - wchar_t errMsg[maxLineSize]; - - this->sym = natural; - overflow = false; - this->numValue = 0; - while (Numeric(this->ch) || Alpha(this->ch) || this->ch == L'_') { - if (Numeric(this->ch)) { - digit = (int32_t)this->ch - (int32_t)'0'; - if (this->numValue <= (shortMax - digit)/10) - this->numValue = 10*this->numValue + digit; - else - overflow = true; - } else - this->ErrorMsg(lexical,L"invalid character in number (can be digits 0 through 9 only)"); - this->GetCh(); - } - this->numValue *= one6; - if (this->ch == L'.' && this->ch2 != L'.') { // permit ppem ranges such as 12..18 - this->GetCh(); - this->sym = rational; - decPlcs = 0; pwrOf10 = 1; - while (Numeric(this->ch) || Alpha(this->ch) || this->ch == L'_') { - if (Numeric(this->ch)) { - digit = (int32_t)this->ch - (int32_t)'0'; - if (decPlcs <= (1000000L - digit)/10) { // 1/64 = 0.015625 - decPlcs = 10*decPlcs + digit; pwrOf10 *= 10L; - } else - overflow = true; - } else - this->ErrorMsg(lexical,L"invalid character in number (can be digits 0 through 9 only)"); - this->GetCh(); - } - this->numValue += (decPlcs*one6 + pwrOf10/2)/pwrOf10; - } - if (overflow) { - swprintf(errMsg,L"number too large (cannot be %li or larger in magnitude)",shortMax+1); this->ErrorMsg(syntactical,errMsg); - } -} /* TMTSourceParser::GetNumber */ - -Symbol Search(wchar_t *entry, short left, short right, short *matching); -Symbol Search(wchar_t *entry, short left, short right, short *matching) { - short mid,diff = 0,minMatch; - wchar_t *id,*en; - - while (left <= right) { - mid = (left + right)/2; - id = tmtCmd[mid].name; minMatch = tmtCmd[mid].minMatch; *matching = 0; en = entry; - while (*matching < minMatch && !(diff = Cap(*id) - Cap(*en))) {(*matching)++; id++; en++;} - if (diff > 0) right = mid - 1; - else if (diff < 0) left = mid + 1; - else return (Symbol)mid; // found - } - return invalid; // not found -} /* Search */ - -void TMTSourceParser::GetIdent(void) { - wchar_t id[idLen],*replId; - short i,matching,origLen,replLen; - bool textReplace; - - i = origLen = 0; - while (Alpha(this->ch) || Numeric(this->ch) || this->ch == L'_') { - if (this->ch != L'_' && i < idLen-1) id[i++] = this->ch; - origLen++; this->GetCh(); - } - id[i] = '\0'; - this->sym = Search(id,0,idents-1,&matching); - if (this->sym != invalid) { - replId = tmtCmd[this->sym].name; - replLen = (short)STRLENW(replId); - for (matching = 0; matching < replLen && id[matching] == replId[matching]; matching++); - textReplace = matching < replLen || i > replLen; // original contains extra characters - if (this->sym == xInterpolate0 || this->sym == xInterpolate1) { this->sym = xInterpolate; replLen = (short)STRLENW(tmtCmd[this->sym].name); textReplace = true; } else - if (this->sym == yInterpolate0 || this->sym == yInterpolate1) { this->sym = yInterpolate; replLen = (short)STRLENW(tmtCmd[this->sym].name); textReplace = true; } - if (textReplace) this->ReplAtCurrPos(origLen,tmtCmd[this->sym].name); - } -} /* TMTSourceParser::GetIdent */ - -void TMTSourceParser::GetLiteral(void) { - bool overflow; - short i; - wchar_t errMsg[maxLineSize]; - - this->sym = literal; - this->GetCh(); - overflow = false; - i = 0; - while (this->ch && this->ch != L'"') { - if (i < maxAsmSize-1) - this->litValue[i++] = this->ch; - else - overflow = true; - this->GetCh(); - } - this->litValue[i++] = '\0'; - - if (!this->ch) this->ErrorMsg(special,L"string quoted but not unquoted"); - this->GetCh(); - if (overflow) { - swprintf(errMsg,L"string too long (cannot be longer than %li characters)",maxAsmSize-1); this->ErrorMsg(syntactical,errMsg); - } -} - -void TMTSourceParser::GetSym(void) { - this->SkipWhiteSpace(true); - this->prevPrevPos = this->prevPos; this->prevPos = this->pos-chLookAhead; - if (Numeric(this->ch)) { - this->GetNumber(); - } else if (Alpha(this->ch)) { - this->GetIdent(); - } else { - switch (this->ch) { - case L'(': this->sym = leftParen; this->GetCh(); break; - case L'[': this->sym = leftParen; this->GetCh(); this->ReplAtCurrPos(1,L"("); break; - case L')': this->sym = rightParen; this->GetCh(); break; - case L']': this->sym = rightParen; this->GetCh(); this->ReplAtCurrPos(1,L")"); break; - - case 0xB3: this->sym = atLeast; this->GetCh(); this->ReplAtCurrPos(1,L">="); break; // replace Mac special char - case L'+': this->sym = plus; this->GetCh(); break; - case L'-': this->sym = minus; this->GetCh(); break; - case L'*': this->sym = timeS; this->GetCh(); break; - case L':': this->sym = colon; this->GetCh(); break; - case L'%': this->sym = percent; this->GetCh(); break; - case L',': this->sym = comma; this->GetCh(); break; - case L';': this->sym = semiColon; this->GetCh(); break; - case L'@': this->sym = aT; this->GetCh(); break; - case L'<': - this->GetCh(); - if (this->ch == L'|') { - this->sym = optStrokeLeftBias; - this->GetCh(); - this->ReplAtCurrPos(2,L"|<"); - } else { - this->sym = leftDir; - } - break; - case L'^': this->sym = upDir; this->GetCh(); break; - case L'>': - this->GetCh(); - if (this->ch == L'=') { - this->sym = atLeast; - this->GetCh(); - } else if (this->ch == L'|') { - this->sym = optStrokeRightBias; - this->GetCh(); - } else { - this->sym = rightDir; - } - break; - case L'/': - this->GetCh(); - if (this->ch == L'/') { - this->sym = adjItalAngle; - this->GetCh(); - } else { - this->sym = italAngle; - } - break; - case L'|': - this->GetCh(); - if (this->ch == L'|') { - this->sym = optStroke; - this->GetCh(); - } else if (this->ch == L'<') { - this->sym = optStrokeLeftBias; - this->GetCh(); - } else if (this->ch == L'>') { - this->sym = optStrokeRightBias; - this->GetCh(); - this->ReplAtCurrPos(2,L">|"); - } else { - this->sym = invalid; - } - break; - case 0xAF: this->sym = adjItalAngle; this->GetCh(); this->ReplAtCurrPos(1,L"//"); break; // replace Mac special char - case L'$': this->sym = postRound; this->GetCh(); break; - case 0xA8: this->sym = postRound; this->GetCh(); this->ReplAtCurrPos(1,L"$"); break; // replace Mac special char - case L'"': this->GetLiteral(); break; - case L'.': - this->GetCh(); - if (this->ch == L'.') { - this->sym = ellipsis; - this->GetCh(); - } else { - this->sym = period; - } - break; - case '\x0': this->sym = eot; break; - default: - this->ErrorMsg(lexical,L"unknown character"); - break; - } - } -} /* TMTSourceParser::GetSym */ - -void TMTSourceParser::Delete(int32_t pos, int32_t len) { - if (len > 0) { - this->talkText->Delete(pos,pos + len); - if (this->pos > pos + len) this->pos -= len; - else if (this->pos > pos) this->pos = pos; - this->changedSrc = true; - } -} /* TMTSourceParser::Delete */ - -void TMTSourceParser::Insert(int32_t pos, const wchar_t strg[]) { - int32_t len; - - len = (int32_t)STRLENW(strg); - if (len > 0) { - this->talkText->Insert(pos,strg); - if (this->pos > pos) this->pos += len; - this->changedSrc = true; - } -} /* TMTSourceParser::Insert */ - -void TMTSourceParser::ReplAtCurrPos(short origLen, const wchar_t repl[]) { - int32_t pos; - - pos = this->pos-chLookAhead-origLen; - this->Delete(pos,origLen); - this->Insert(pos,repl); -} /* TMTSourceParser::ReplAtCurrPos */ - -void TMTSourceParser::ErrorMsg(short kind, const wchar_t errMsg[]) { - if (this->errPos < 0) { // no error reported yet - this->ch = this->ch2 = L'\x0'; - this->sym = eot; - if (errMsg[0] == L'\0') - this->errPos = 0; // dummy error to QUIT() compilation... - else { - switch (kind) { - case special: this->errPos = this->pos; this->symLen = this->errPos - this->prevPos; break; - case lexical: this->errPos = this->pos-chLookAhead+1; this->symLen = 1; break; - case syntactical: this->errPos = this->pos-chLookAhead; this->symLen = this->errPos - this->prevPos; break; - case contextual: this->errPos = this->prevPos; this->symLen = this->errPos - this->prevPrevPos; break; - } - STRCPYW(this->errMsg,errMsg); - } - } -} /* TMTSourceParser::ErrorMsg */ - -TMTParser *NewTMTSourceParser(void) { - return new TMTSourceParser; -} - -bool TMTCompile(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TextBuffer *glyfText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]) { - TTEngine *ttengine; // should think of keeping these around somewhere w/o having to allocate over and over again... - short generators,i; - TTGenerator *ttgenerator[3]; - TMTParser *tmtparser; - bool memError,changedSrc; - - if (glyph->componentSize > 0) return true; // don't touch composites... - - tmtparser = NewTMTSourceParser(); memError = !tmtparser; - generators = 0; - if (glyfText) { - ttgenerator[generators] = NewTTSourceGenerator(); - if (!ttgenerator[generators] ) memError = true; else generators++; - } -/***** - if (grafWindow) { - ttgenerator[generators] = NewTTGlyphStrGenerator(); - if (!ttgenerator[generators] ) memError = true; else generators++; - } -*****/ - if (glyfText) { - ttengine = NewTTSourceEngine(); - memError = memError || !ttengine; - } else - ttengine = NULL; - if (ttengine && !memError) ttengine->InitTTEngine(legacyCompile, &memError); - for (i = 0; i < generators && !memError; i++) ttgenerator[i]->InitTTGenerator(font,glyph,glyphIndex,ttengine,legacyCompile,&memError); - if (!memError) { - tmtparser->InitTMTParser(talkText,font,glyph,legacyCompile,generators,ttgenerator); - tmtparser->Parse(&changedSrc,errPos,errLen,errMsg); - tmtparser->TermTMTParser(); - for (i = 0; i < generators; i++) ttgenerator[i]->TermTTGenerator(); - if (ttengine) ttengine->TermTTEngine(*errPos < 0 ? glyfText : NULL,&memError); - } - if (ttengine) delete ttengine; - for (i = 0; i < generators; i++) delete ttgenerator[i]; - if (tmtparser) delete tmtparser; - - if (memError) swprintf(errMsg,L"Insufficient memory for compilation"); - else if (*errPos > 0) *errPos -= *errLen; - return !memError && *errPos + *errLen <= 0; -} /* TMTCompile */ - -#if _DEBUG -bool TMTRemoveAltCodePath(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]) { - TMTParser *tmtparser; - bool memError,changedSrc; - - if (glyph->componentSize > 0) return true; // don't touch composites... - - tmtparser = NewTMTSourceParser(); memError = !tmtparser; - if (!memError) { - tmtparser->InitTMTParser(talkText,font,glyph,false, 0,NULL); - tmtparser->RemoveAltCodePath(&changedSrc,errPos,errLen,errMsg); - tmtparser->TermTMTParser(); - } - if (tmtparser) delete tmtparser; - - if (memError) swprintf(errMsg,L"Insufficient memory for compilation"); - else if (*errPos > 0) *errPos -= *errLen; - return !memError && *errPos + *errLen <= 0; -} // TMTRemoveAltCodePath -#endif - - -/* the flickering at the end of the compilation comes from a flag (edit_saveOnCharChange) - that forces every change to be saved (with the ominous double fs_DoFile...) and subse- - quently reloaded, which in turn causes an update event for all visible windows (at least - the main-, tmt-, and tt-window), and apparently, doing a TEInsert causes an update of - the respective window as well, hence the tt-window gets updated twice (once before the - save). - For the future, might think of making such things more consistent: remember what - has changed, or that something has changed, and save changes only upon request (alerting - the uncautious user of unsaved data upon close). Should poll the users what they want, - explaining the pros (speed, flickering) and cons (computer failure induced loss of data). - - looking at all the /one6 statements, it might turn out to be more sensible to come back - with these values the way they have to be, such as in ValidateParam. - - Another question raised (by Claude): wouldn't it be possible to check, whether an in- - formative command does preceed the action command(s) it is supposed to influence. I think - with the GUI to come, and its data structure, this will come for free, so I'd rather not - duplicate work now. - - An idea in this area is to get rid of having to use X|YRound, X|YStroke, but instead make - the compiler/code generator more intelligent: if there is no cvt override, and if there - are no informative commands upon generating code for a Link, then try to obtain the fea- - ture category (cvtRound, cvtStroke, cvtDistance) automatically. Should be fairly simple for - rounds, may have to extract some code from the auto-hinter for determining the strokes. - - Seems like the values for the overshoots are actually relative to the caps line (etc.), - ending up doing much like what I do: actualHeight = capsLine + overshoot - - Why are there different values for the cvt_cut_in <27, 27-68, >68 ppem??? - - In any case the X|YStroke had probably better be called X|YStem and expect 2 knots only, - rather than 4, much like the X|YRound, or an even number of parameters? - -*/ +/***** + + TMTParser.c - New TypeMan Talk Compiler - Parser + + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +*****/ +#define _CRT_SECURE_NO_DEPRECATE + +#include // wcslen +#include // printf + +#include "pch.h" +#include "List.h" +#include "TTEngine.h" +#include "TTGenerator.h" +#include "TMTParser.h" + +#define idents invalid +#define compLogics 2L +#define phases 4L +#define serifs 5L +#define idLen 32L +#define maxFPs 10L +#define maxAngle 18000L /* half a full circle at 100 units per degree */ +#define shortMax 0x7fffL +#define special 0 +#define lexical 1 +#define syntactical 2 +#define contextual 3 +#define chLookAhead 2 + +#define WhiteSpace(self) (L'\x0' < self->ch && self->ch <= L' ') +#define InitComment(self) (self->ch == L'/' && self->ch2 == L'*') +#define TermComment(self) (self->ch == L'*' && self->ch2 == L'/') +#define Numeric(ch) (L'0' <= (ch) && (ch) <= L'9') +#define Alpha(ch) ((L'A' <= (ch) && (ch) <= L'Z') || (L'a' <= (ch) && (ch) <= L'z')) +#define Command(self) (align <= (self)->sym && (self)->sym <= invalid) +#define InitFlag(self) (leftDir <= (self)->sym && (self)->sym <= postRound) +#define InitParam(self) (invalid <= (self)->sym && (self)->sym <= leftDir) // leftDir included because it doubles up as lessThan in the minDistGeneral parameter +#define Separator(self) (period <= (self)->sym && (self)->sym <= semiColon) +#define InterpolateCmd(cmd) ((cmd) == xInterpolate || (cmd) == xInterpolate0 || (cmd) == xInterpolate1 || (cmd) == xIPAnchor || (cmd) == yInterpolate || (cmd) == yInterpolate0 || (cmd) == yInterpolate1 || (cmd) == yIPAnchor) +#define maxGenerators 3 + +void TMTParser::Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen) { /* abstract */ } +#if _DEBUG +void TMTParser::RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[], size_t errorLen) { /* abstract */ } +#endif +void TMTParser::InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]) { /* abstract */ } +void TMTParser::TermTMTParser(void) { /* abstract */ } +TMTParser::TMTParser(void) { /* abstract */ } +TMTParser::~TMTParser(void) { /* abstract */ } + +/* note: this and all following enumeration typedefs should be assumed to be ordered */ +typedef enum { align = 0, asM, autoHinter, + beginCodePath, // COM + call, compilerLogic, cvtAllocation, cvtLogic, + dAlign, diagEndCtrl, diagonalMT, diagSerifs, dStroke, + endCodePath, // COM + fixDStrokes, glyphStrokeAngle, grabHereInX, height, InLine, intersect, iStroke, mainStrokeAngle, processXSymmetrically, processYSymmetrically, quit, + resIIPDDist, resIIPDLink, // COM + resXAnchor, resXDDist, resXDist, resXDLink, resXIPAnchor, resXIPDDDist, resXIPDDist, resXIPDDLink, resXIPDist, resXIPDLink, resXIPLink, resXLink, // COM + resYAnchor, resYDDist, resYDist, resYDLink, resYIPAnchor, resYIPDDDist, resYIPDDist, resYIPDDLink, resYIPDist, resYIPDLink, resYIPLink, resYLink, // COM + scoop, serif, setItalicStrokeAngle, setItalicStrokePhase, smooth, stroke, tail, tweakMetrics, vacuFormLimit, vacuFormRound, + //xAlign, xAnchor, xBDelta, xDelta, xDiagonal, xDist, xDoubleGrid, xDownToGrid, xGDelta, xHalfGrid, xInterpolate, xInterpolate0, xInterpolate1, xIPAnchor, xLink, xMove, xNoRound, xRound, xShift, xSmooth, xStem, xStroke, xUpToGrid, + xAlign, xAnchor, xBDelta, xCDelta, xDelta, xDiagonal, xDist, xDoubleGrid, xDownToGrid, xGDelta, xHalfGrid, xInterpolate, xInterpolate0, xInterpolate1, xIPAnchor, xLink, xMove, xNoRound, xRound, xShift, xSmooth, xStem, xStroke, xUpToGrid, // MODIFY GREGH + //yAlign, yAnchor, yBDelta, yDelta, yDiagonal, yDist, yDoubleGrid, yDownToGrid, yGDelta, yHalfGrid, yInterpolate, yInterpolate0, yInterpolate1, yIPAnchor, yLink, yMove, yNoRound, yRound, yShift, ySmooth, yStem, yStroke, yUpToGrid, + yAlign, yAnchor, yBDelta, yCDelta, yDelta, yDiagonal, yDist, yDoubleGrid, yDownToGrid, yGDelta, yHalfGrid, yInterpolate, yInterpolate0, yInterpolate1, yIPAnchor, yLink, yMove, yNoRound, yRound, yShift, ySmooth, yStem, yStroke, yUpToGrid, // MODIFY GREGH + invalid, + leftParen, plus, minus, natural, rational, literal, aT, atLeast, + upDir, // COM + leftDir, /* InitParam (including above invalid) */ + rightDir, italAngle, adjItalAngle, + optStroke, optStrokeLeftBias, optStrokeRightBias, // COM + postRound, /* leftDir through postRound: Flags() */ + rightParen, timeS, + period, ellipsis, + colon, // COM + percent, + comma, semiColon, /* period through semiColon: Separator() */ + eot + } Symbol; + +typedef enum { voidParam = 0, + anyN, knotN, + knotNttvOpt, knotNttvOptXY, // COM + cvtN, compLogicN, cvtLogicN, phaseN, angle100N, colorN, serifN, curveN, radiusN, + rationalN, posRationalN, + ppemSize, ppemN, rangeOfPpemN, + rangeOfPpemNcolorOpt, + anyS, + minDistFlagOnly, minDistGeneral, + dirFlag, angleFlag, + strokeFlag, // COM + postRoundFlag} ParamType; + +// #define knotNttvOpt knotN // !COM +// #define knotNttvOptXY knotN // !COM + +typedef enum { mand, opt, optR} Presence; + +typedef struct { + ParamType type; + Presence pres; +} FormParam; + +typedef struct { + ParamType type; + int32_t numValue; + wchar_t *litValue; // pointer to parser's litValue for memory efficiency, since we don't have more than 1 string parameter per TMT command + short minDists; + int32_t jumpPpemSize[maxMinDist],pixelSize[maxMinDist]; + bool deltaPpemSize[maxPpemSize]; // here we have possibly more than one rangeOfPpemN parameter, but could implement bit vectors... + DeltaColor deltaColor; // alwaysDelta, blackDelta, greyDelta, ..., same for the entire bit vector deltaPpemSize above + bool hasTtvOverride; + TTVectorDesc ttvOverride; // for diagDir and perpDiagDir +} ActParam; + +class Height : public ListElem { +public: + Height(void); + virtual ~Height(void); + short of; + short cvtOverride; +}; + +class Partner : public ListElem { +public: + Partner(void); + virtual ~Partner(void); + LinkDirection direction; + CvtCategory category; + short of,with; + short cvtOverride; +}; + +Height::Height(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // Height::Height + +Height::~Height(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // Height::~Height + +Partner::Partner(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // Partner::Partner + +Partner::~Partner(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // Partner::~Partner + +class TMTSourceParser : public TMTParser { +public: + virtual void Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen); +#if _DEBUG + virtual void RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[], size_t errorLen); +#endif + virtual void InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]); + virtual void TermTMTParser(void); + TMTSourceParser(void); + virtual ~TMTSourceParser(void); +private: + int32_t errPos,symLen; + wchar_t errMsg[maxLineSize]; + TextBuffer *talkText; + TrueTypeFont *font; + TrueTypeGlyph *glyph; + LinearListStruct *heights,*partners; + short knots; + double tanStraightAngle; + bool italicStrokePhase,italicStrokeAngle,mainStrokeAngle,glyphStrokeAngle; + short generators; + TTGenerator *gen[maxGenerators]; + bool changedSrc; + int32_t pos,prevPos,prevPrevPos; // prevPos = position previous to starting the scanning of current symbol + wchar_t ch,ch2; // 2-char look-ahead + Symbol sym; // 1-symbol look-ahead + int32_t numValue; + bool legacyCompile; + wchar_t litValue[maxAsmSize]; + + short actParams; + ActParam actParam[maxParams]; + int32_t paramPos[maxParams + 1]; // +1 needed to avoid out of bounds error if at max parameters + + bool MakeProjFreeVector(bool haveFlag, int32_t flagValue, bool y, ActParam *parent, ActParam child[], int32_t children, ProjFreeVector *projFreeVector, wchar_t errMsg[], size_t errMsgLen); + + virtual void Dispatch(Symbol cmd, short params, ActParam param[], wchar_t errMsg[], size_t errMsgLen); + + Height *TheHeight(short at); + void RegisterHeight(short at, short cvt); + Partner *ThePartner(bool y, short from, short to); + void RegisterPartner(short from, short to, bool y, bool round, short cvt); + + /***** Scanner *****/ + virtual void GetCh(void); + virtual void SkipComment(void); + virtual void SkipWhiteSpace(bool includingComments); + virtual void GetNumber(void); + virtual void GetIdent(void); + virtual void GetLiteral(void); + virtual void GetSym(void); + + void Delete(int32_t pos, int32_t len); + void Insert(int32_t pos, const wchar_t strg[]); + void ReplAtCurrPos(short origLen, const wchar_t repl[]); + + /***** Parser *****/ + virtual void XFormToNewSyntax(void); + virtual void Flag(ActParam *actParam); + virtual void Parameter(ActParam *actParam); + virtual void MatchParameter(FormParam *formParams, short *formParamNum, ParamType *actParamType); + virtual void ValidateParameter(ActParam *actParam); + virtual void Expression(ActParam *actParam); + virtual void Term(ActParam *actParam); + virtual void Factor(ActParam *actParam); + virtual void MinDist(ActParam *actParam); + virtual void Range(ActParam *actParam); + virtual void PpemRange(ActParam *actParam); + + /***** Common *****/ + /*virtual void Error(short kind, short num);*/ + virtual void ErrorMsg(short kind, const wchar_t errMsg[]); +}; + +typedef struct { + wchar_t name[idLen]; + short minMatch; + FormParam param[maxFPs]; +} CommandDesc; + +CommandDesc tmtCmd[idents] = { + {L"Align", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}}}, + {L"ASM", 2, {{anyS, mand}}}, + {L"AutoHinter", 2, {{anyS, mand}}}, + {L"BeginCodePath", 1, {{anyN, mand}}}, + {L"Call", 2, {{anyN, mand}, {anyN, optR}}}, + {L"CompilerLogic", 2, {{compLogicN, mand}}}, + {L"CvtAllocation", 4, {{anyS, mand}}}, + {L"CvtLogic", 4, {{cvtLogicN, mand}}}, + {L"DAlign", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}, {ppemSize, opt}}}, + {L"DiagEndCtrl", 5, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {anyN, mand}}}, + {L"Diagonal", 5, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, + {L"DiagSerifs", 5, {{knotN, mand}, {knotN, mand}}}, + {L"DStroke", 2, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}}}, + {L"EndCodePath", 1}, + {L"FixDStrokes", 1}, + {L"GlyphStrokeAngle", 2, {{cvtN, mand}, {cvtN, mand}}}, + {L"GrabHereInX", 2, {{knotN, mand}, {knotN, mand}}}, + {L"Height", 1, {{knotN, mand}, {cvtN, mand}}}, + {L"Inline", 3, {{anyS, mand}}}, + {L"Intersect", 3, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {ppemSize, opt}, {ppemSize, opt}}}, + {L"IStroke", 2, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {cvtN, mand}, {phaseN, mand}, {cvtN, opt}}}, + {L"MainStrokeAngle", 1, {{angle100N, mand}}}, + {L"ProcessXSymmetrically", 8}, + {L"ProcessYSymmetrically", 8}, + {L"Quit", 1}, + + {L"ResIIPDDist", 8, {{knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // for the time being, we'll assume that parents and children have been constrained (moved) in y... + {L"ResIIPDLink", 8, {{knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN, mand}}}, // ... before calling ResIIPDDist/Link, hence we don't allow fv overrides + + {L"ResXAnchor", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {cvtN, opt}}}, // see also TMTSourceParser::Dispatch wherein the mandatory knot is considered a child point hence the ttvOpt override designates an alternate fv (unless angleFlag is used) + {L"ResXDDist", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResXDist", 6, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistFlagOnly, opt}}}, + {L"ResXDLink", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResXIPAnchor", 7, {{angleFlag, opt}, {postRoundFlag, opt}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // for the time being, we only allow one child, hence last parameter is a parent, which does not have to override the pv again + {L"ResXIPDDDist", 9, {{angleFlag, opt}, {knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResXIPDDist", 9, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, // for lc 'm' could use a TDist version, so think about generalizing to a MDist version + {L"ResXIPDDLink", 9, {{angleFlag, opt}, {knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResXIPDist", 8, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // currently we don't allow to override the fv of the parent (!) nor the child... + {L"ResXIPDLink", 8, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}}}, // for lc 'm' could use a TLink version, so think about generalizing to a MLink version + {L"ResXIPLink", 7, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN, mand}}}, // ...generalizing this could become a parameter-passing nightmare + {L"ResXLink", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, mand}, {minDistFlagOnly, opt}}}, + + {L"ResYAnchor", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {cvtN, opt}}}, // see also TMTSourceParser::Dispatch wherein the mandatory knot is considered a child point hence the ttvOpt override designates an alternate fv (unless angleFlag is used) + {L"ResYDDist", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResYDist", 6, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistFlagOnly, opt}}}, + {L"ResYDLink", 6, {{knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResYIPAnchor", 7, {{angleFlag, opt}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // for the time being, we only allow one child, hence last parameter is a parent, which does not have to override the pv again + {L"ResYIPDDDist", 9, {{knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResYIPDDist", 9, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, // for lc 'm' could use a TDist version, so think about generalizing to a MDist version + {L"ResYIPDDLink", 9, {{knotN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}, {knotNttvOptXY, mand}, {cvtN, mand}, {knotN, mand}}}, // only children get to override fv, including x to y and v.v. though + {L"ResYIPDist", 8, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {knotN, mand}}}, // currently we don't allow to override the fv of the parent (!) nor the child... + {L"ResYIPDLink", 8, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, mand}, {knotN, mand}}}, // for lc 'm' could use a TLink version, so think about generalizing to a MLink version + {L"ResYIPLink", 7, {{angleFlag, opt}, {strokeFlag, opt}, {knotN, mand}, {knotN/*ttvOpt*/, mand}, {knotN/*ttvOpt*/, mand}, {cvtN, mand}, {knotN, mand}}}, // ...generalizing this could become a parameter-passing nightmare + {L"ResYLink", 5, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, mand}, {minDistFlagOnly, opt}}}, + + {L"Scoop", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}}}, + {L"Serif", 3, {{dirFlag, mand}, {serifN, mand}, {knotN, mand}}}, // context dependent number of knots adjusted on-the-fly in AdjustFPs + {L"SetItalicStrokeAngle", 16}, + {L"SetItalicStrokePhase", 16}, + {L"Smooth", 2, {{angleFlag, opt}}}, // !COM {L"Smooth", 2}, + {L"Stroke", 2, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, + {L"Tail", 2, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, + {L"TweakMetrics", 2}, + {L"VacuFormLimit", 9, {{ppemSize, mand}}}, + {L"VacuFormRound", 9, {{dirFlag, mand}, {dirFlag, mand}, {curveN, mand}, {radiusN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}}}, + + {L"XAlign", 3, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}, {ppemSize, opt}}}, + {L"XAnchor", 3, {{angleFlag, opt}, {knotNttvOpt, mand}}}, + {L"XBDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, + {L"XCDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, + {L"XDelta", 3, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, + {L"XDiagonal", 4, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, + {L"XDist", 4, {{angleFlag, opt}, {postRoundFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistGeneral, opt}}}, + {L"XDoubleGrid", 4, {{knotN, mand}, {knotN, optR}}}, + {L"XDownToGrid", 4, {{knotN, mand}, {knotN, optR}}}, + {L"XGDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, + {L"XHalfGrid", 2, {{knotN, mand}, {knotN, optR}}}, + {L"XInterpolate", 3, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... + {L"XInterpolate0", 13}, + {L"XInterpolate1", 13}, + {L"XIPAnchor", 4, {{angleFlag, opt}, {postRoundFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... + {L"XLink", 2, {{angleFlag, opt}, {postRoundFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, opt}, {minDistGeneral, opt}}}, + {L"XMove", 2, {{rationalN, mand}, {knotN, mand}, {knotN, optR}}}, + {L"XNoRound", 2, {{knotN, mand}, {knotN, optR}}}, + {L"XRound", 2, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, + {L"XShift", 3, {{angleFlag, opt}, {knotN, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // shift never respects the pv + {L"XSmooth", 3, {{angleFlag, opt}}}, // !COM {L"XSmooth", 3}, + {L"XStem", 4, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, + {L"XStroke", 4, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}}}, + {L"XUpToGrid", 2, {{knotN, mand}, {knotN, optR}}}, + + {L"YAlign", 3, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, optR}, {ppemSize, opt}}}, + {L"YAnchor", 3, {{angleFlag, opt}, {knotNttvOpt, mand}, {cvtN, opt}}}, + {L"YBDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, + {L"YCDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, // ADD GREGH + {L"YDelta", 3, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, + {L"YDiagonal", 4, {{dirFlag, mand}, {dirFlag, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}, {ppemSize, opt}}}, + {L"YDist", 4, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {minDistGeneral, opt},}}, + {L"YDoubleGrid", 4, {{knotN, mand}, {knotN, optR}}}, + {L"YDownToGrid", 4, {{knotN, mand}, {knotN, optR}}}, + {L"YGDelta", 2, {{knotN, mand}, {rangeOfPpemNcolorOpt, mand}, {rangeOfPpemNcolorOpt, optR}}}, + {L"YHalfGrid", 2, {{knotN, mand}, {knotN, optR}}}, + {L"YInterpolate", 3, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... + {L"YInterpolate0", 13}, + {L"YInterpolate1", 13}, + {L"YIPAnchor", 4, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // last actual parameter set to knotN in TMTSourceParser::Parse... + {L"YLink", 2, {{angleFlag, opt}, {knotNttvOpt, mand}, {knotNttvOpt, mand}, {cvtN, opt}, {minDistGeneral, opt}}}, + {L"YMove", 2, {{rationalN, mand}, {knotN, mand}, {knotN, optR}}}, + {L"YNoRound", 2, {{knotN, mand}, {knotN, optR}}}, + {L"YRound", 2, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, + {L"YShift", 3, {{angleFlag, opt}, {knotN, mand}, {knotNttvOpt, mand}, {knotNttvOpt, optR}}}, // shift never respects the pv + {L"YSmooth", 3, {{angleFlag, opt}}}, // !COM {L"YSmooth", 3}, + {L"YStem", 4, {{knotN, mand}, {knotN, mand}, {cvtN, opt}}}, + {L"YStroke", 4, {{knotN, mand}, {knotN, mand}, {knotN, mand}, {knotN, mand}, {cvtN, opt}}}, + {L"YUpToGrid", 2, {{knotN, mand}, {knotN, optR}}}, +}; + +FormParam minDistParam[maxFPs] = {{posRationalN, mand}, {ppemSize, mand}, {posRationalN, mand}}; // auxiliary parameter lists +FormParam rangeOfPpemNParam[maxFPs] = {{rationalN, mand}, {ppemN, mand}, {ppemN, mand}}; // for recursively calling MatchParameter +FormParam deltaColorParam[maxFPs] = {{colorN, mand}}; +FormParam knotNttvOptParams[maxFPs] = {{knotN, mand}, {knotN, mand}, {knotN, mand}}; + +void AdjustFPs(short serifType, FormParam *formParams); +void AdjustFPs(short serifType, FormParam *formParams) { + /* SERIF < (type, knot0, knot1, ... knotn-1) + param 0 1, 2, 3, param-1 */ + short params = 0,i; + + switch (serifType) { + case 0: params = 9; break; + case 1: + case 2: + case 3: params = 6; break; + case 4: params = 8; break; + } + for (i = 3; i < params; i++) formParams[i] = formParams[2]; + for (i = params; i < maxFPs-1; i++) formParams[i] = formParams[maxFPs-1]; +} /* AdjustFPs */ + +void TMTSourceParser::Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen) { + Symbol cmd; + ActParam aParam; + short formParamNum; + FormParam formParams[maxFPs]; + int32_t cmdStart,actParam; + + this->GetSym(); + while (this->errPos < 0 && Command(this)) { + cmdStart = this->prevPos; + cmd = this->sym; + if (cmd == invalid) this->ErrorMsg(syntactical,L"unknown VTT Talk command"); + if (cmd == xAnchor || cmd == xInterpolate || cmd == xIPAnchor || cmd == xLink || + cmd == yAnchor || cmd == yInterpolate || cmd == yIPAnchor || cmd == yLink) this->XFormToNewSyntax(); + this->GetSym(); + /***** + if (cmd == ::mainStrokeAngle || cmd == ::glyphStrokeAngle || cmd == setItalicStrokeAngle || cmd == setItalicStrokePhase) { + if (cmd == ::mainStrokeAngle && this->mainStrokeAngle || cmd == ::glyphStrokeAngle && this->glyphStrokeAngle || cmd == setItalicStrokeAngle && this->italicStrokeAngle || cmd == setItalicStrokePhase && this->italicStrokePhase) { + swprintf(errMsg,WIDE_STR_FORMAT L" already used in this glyph",tmtCmd[cmd].name); this->ErrorMsg(contextual,errMsg); + } + if (cmd == ::mainStrokeAngle && this->glyphStrokeAngle || cmd == ::glyphStrokeAngle && this->mainStrokeAngle) { + swprintf(errMsg,L"Cannot use both " WIDE_STR_FORMAT L" and " WIDE_STR_FORMAT L" in the same glyph",tmtCmd[::mainStrokeAngle].name,tmtCmd[::glyphStrokeAngle].name); this->ErrorMsg(contextual,errMsg); + } + if (cmd == ::mainStrokeAngle) this->mainStrokeAngle = true; + else if (cmd == ::glyphStrokeAngle) this->glyphStrokeAngle = true; + else if (cmd == setItalicStrokeAngle) this->italicStrokeAngle = true; + else if (cmd == setItalicStrokePhase) this->italicStrokePhase = true; + } + *****/ + // formParamNum = 0; formParams = &tmtCmd[cmd].param[0]; + for (formParamNum = 0; formParamNum < maxFPs; formParams[formParamNum] = tmtCmd[cmd].param[formParamNum], formParamNum++); + formParamNum = 0; + this->actParams = 0; + while (InitFlag(this)) { + this->paramPos[this->actParams] = this->prevPrevPos; + this->Flag(&aParam); this->MatchParameter(formParams,&formParamNum,&aParam.type); this->ValidateParameter(&aParam); + if (this->actParams < maxParams) + this->actParam[this->actParams++] = aParam; + else { + swprintf(errMsg,errMsgLen,L"too many actual parameters (cannot have more than %li parameters)",maxParams); + this->ErrorMsg(contextual,errMsg); + } + } + if (this->sym == leftParen) this->GetSym(); else this->ErrorMsg(syntactical,L"( expected"); + if (InitParam(this)) { + this->paramPos[this->actParams] = this->prevPrevPos; + this->Parameter(&aParam); + this->MatchParameter(formParams,&formParamNum,&aParam.type); + this->ValidateParameter(&aParam); + if (this->actParams < maxParams) + this->actParam[this->actParams++] = aParam; + else { + swprintf(errMsg,errMsgLen,L"too many actual parameters (cannot have more than %li parameters)",maxParams); + this->ErrorMsg(contextual,errMsg); + } + if (cmd == serif) AdjustFPs((short)(aParam.numValue/one6),formParams); // make the remaining formal parameters compatible with the actual serif type... + while (Separator(this) || InitParam(this)) { + if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); + if (Separator(this)) this->GetSym(); + this->paramPos[this->actParams] = this->prevPrevPos; + this->Parameter(&aParam); + if (!this->legacyCompile && InterpolateCmd(cmd) && !Separator(this) && !InitParam(this)) formParams[formParamNum].type = knotN; // drop ttvOpt of other parent + this->MatchParameter(formParams,&formParamNum,&aParam.type); + this->ValidateParameter(&aParam); + if (this->actParams < maxParams) + this->actParam[this->actParams++] = aParam; + else { + swprintf(errMsg,errMsgLen,L"too many actual parameters (cannot have more than %li parameters)",maxParams); + this->ErrorMsg(contextual,errMsg); + } + } + } + while (formParamNum < maxFPs && opt <= formParams[formParamNum].pres && formParams[formParamNum].pres <= optR) formParamNum++; + if (formParamNum < maxFPs && formParams[formParamNum].type != voidParam) this->ErrorMsg(contextual,L"not enough parameters"); + if (this->sym == rightParen) this->GetSym(); else this->ErrorMsg(syntactical,L") expected"); + for (actParam = this->actParams; actParam < maxParams; actParam++) + this->paramPos[actParam] = this->prevPrevPos; + if (this->generators > 0 && this->errPos < 0) { + this->prevPrevPos = cmdStart; + this->Dispatch(cmd,this->actParams,this->actParam,errMsg,errMsgLen); + if (errMsg[0]) this->ErrorMsg(contextual,errMsg); + } + if (cmd == quit) this->ErrorMsg(syntactical,L""); // generate dummy error to drop out of loop the same way as errors + } + if (this->sym != eot) this->ErrorMsg(syntactical,L"VTT Talk command expected"); + *changedSrc = this->changedSrc; + *errPos = this->errPos; + *errLen = this->symLen; + STRCPYW(errMsg,this->errMsg); +} /* TMTSourceParser::Parse */ + +#if _DEBUG +void TMTSourceParser::RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[], size_t errorLen) { + int32_t beginPos,endPos; + + beginPos = endPos = -1; *changedSrc = false; + this->GetSym(); + while (this->sym != eot && this->errPos < 0 && !(beginPos >= 0 && endPos >= 0)) { + if (beginPos < 0 && this->sym == beginCodePath) { + this->GetSym(); this->GetSym(); this->GetSym(); this->SkipWhiteSpace(false); + beginPos = this->pos - chLookAhead; + } + if (endPos < 0 && this->sym == endCodePath) { + endPos = this->prevPos; + } + this->GetSym(); + } + if (beginPos >= 0 && endPos >= 0) { + this->talkText->Delete(endPos,this->talkText->TheLength()); + this->talkText->Delete(0,beginPos); + *changedSrc = true; + } + *errPos = this->errPos; + *errLen = this->symLen; +} // TMTSourceParser::RemoveAltCodePath +#endif + + +TMTSourceParser::TMTSourceParser(void) { + this->tanStraightAngle = tan(Rad(STRAIGHTANGLEFUDGE)); + this->italicStrokePhase = this->italicStrokeAngle = this->mainStrokeAngle = this->glyphStrokeAngle = false; + this->heights = new LinearListStruct; + this->partners = new LinearListStruct; + this->legacyCompile = false; +} // TMTSourceParser::TMTSourceParser + +TMTSourceParser::~TMTSourceParser(void) { + if (this->partners) delete this->partners; + if (this->heights) delete this->heights; +} // TMTSourceParser::~TMTSourceParser + +Height *TMTSourceParser::TheHeight(short at) { + Height *height; + + height = NULL; + if (this->heights) + for (height = (Height*)this->heights->first; height && height->of != at; height = (Height*)height->next); + return height; +} // TMTSourceParser::TheHeight + +void TMTSourceParser::RegisterHeight(short at, short cvt) { + Height *height; + bool found; + + height = this->TheHeight(at); + found = height != NULL; + if (!found) height = new Height; // else overwrite like GUI would + if (height) { + height->of = at; + height->cvtOverride = cvt; + if (!found) this->heights->InsertAtEnd(height); + } +} // TMTSourceParser::RegisterHeight + +Partner *TMTSourceParser::ThePartner(bool y, short from, short to) { + Partner *partner; + + partner = NULL; + if (this->partners) + for (partner = (Partner*)this->partners->first; partner && !((partner->of == from && partner->with == to) || (partner->of == to && partner->with == from)); partner = (Partner*)partner->next); + return partner; +} // TMTSourceParser::ThePartner + +void TMTSourceParser::RegisterPartner(short from, short to, bool y, bool round, short cvt) { + Partner *partner; + bool found; + + partner = this->ThePartner(y,from,to); + found = partner != NULL; + if (!found) partner = new Partner; // else overwrite like GUI would + if (partner) { + partner->direction = y ? linkY : linkX; + partner->category = round ? cvtRound : cvtStroke; + partner->of = from; partner->with = to; + partner->cvtOverride = cvt; + if (!found) this->partners->InsertAtEnd(partner); + } +} // TMTSourceParser::RegisterPartner + +void TMTSourceParser::InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]) { + short i; + + this->errPos = -1; this->symLen = 0; + this->errMsg[0] = L'\0'; + this->talkText = talkText; + this->font = font; + this->glyph = glyph; + this->knots = glyph->numContoursInGlyph > 0 ? glyph->endPoint[glyph->numContoursInGlyph-1] + 1 : 0; + this->knots += PHANTOMPOINTS; + this->generators = generators; + for (i = 0; i < generators; i++) this->gen[i] = gen[i]; + this->changedSrc = false; + this->pos = this->prevPos = this->prevPrevPos = 0; + this->ch2 = 0; // silence BC + this->legacyCompile = legacyCompile; + this->GetCh(); + this->GetCh(); +} /* TMTSourceParser::InitTMTParser */ + +void TMTSourceParser::TermTMTParser(void) { + +} /* TMTSourceParser::TermTMTParser */ + +bool TMTSourceParser::MakeProjFreeVector(bool haveFlag, int32_t flagValue, bool y, ActParam *parent, ActParam child[], int32_t children, ProjFreeVector *projFreeVector, wchar_t errMsg[], size_t errMsgLen) { + TTVDirection flagToDir[numTTVDirections] = {xRomanDir, yRomanDir, xItalDir, yItalDir, xAdjItalDir, yAdjItalDir, diagDir, perpDiagDir}; + int32_t i,idx = 2*(haveFlag ? 1 + flagValue : 0) + (int32_t)y; + bool pvOverrideError = false,fvOverrideError = false; + + projFreeVector->pv.dir = flagToDir[idx%numTTVDirections]; + projFreeVector->pv.from = projFreeVector->pv.to = invalidKnotNum; + for (i = 0; i < children; i++) projFreeVector->fv[i] = projFreeVector->pv; + if (!this->legacyCompile) + { + + if (parent != NULL && parent->hasTtvOverride) { // NULL for Anchors, they have no parents + projFreeVector->pv = parent->ttvOverride; + if (projFreeVector->pv.from == invalidKnotNum) projFreeVector->pv.from = (short) (parent->numValue / one6); + if (!y && haveFlag) pvOverrideError = true; + } + for (i = 0; i < children && !pvOverrideError && !fvOverrideError; i++) { + if (child[i].hasTtvOverride) { + projFreeVector->fv[i] = child[i].ttvOverride; + if (projFreeVector->fv[i].from == invalidKnotNum) projFreeVector->fv[i].from = (short) (child[i].numValue / one6); + if (y && haveFlag) fvOverrideError = true; + } + } + if (pvOverrideError || fvOverrideError) + swprintf(errMsg, errMsgLen, L"cannot override " WIDE_STR_FORMAT L" direction when using the italic or adjusted italic angle / or //", pvOverrideError ? L"projection" : L"freedom"); + } + return !(pvOverrideError || fvOverrideError); +} // TMTSourceParser::MakeProjFreeVector + +void TMTSourceParser::Dispatch(Symbol cmd, short params, ActParam param[], wchar_t errMsg[], size_t errMsgLen) { + short i,j; + + errMsg[0] = L'\0'; + switch (cmd) { + case align: + case xAlign: + case yAlign: + case dAlign: { + short ppem = param[params-1].type == ppemSize ? (short)(param[params-1].numValue/one6) : -1, + children = params - (2 + (ppem >= 0)), // 2 parents... + parent0 = (short)(param[0].numValue/one6),parent1 = (short)(param[children+1].numValue/one6),child[maxParams]; + FVOverride fvOverride = cmd == align ? fvOldMethod : (cmd == xAlign ? fvForceX : (cmd == yAlign ? fvForceY : fvStandard)); + + for (i = 0; i < children; i++) child[i] = (short)(param[1+i].numValue/one6); + for (i = 0; i < this->generators; i++) this->gen[i]->Align(fvOverride,parent0,children,child,parent1,ppem,errMsg,errMsgLen); + break; + } + case asM: + case InLine: + for (i = 0; i < this->generators; i++) this->gen[i]->Asm(cmd == InLine,param[0].litValue,errMsg,errMsgLen); + break; + case autoHinter: + break; + case call: { + short anyNum[maxParams]; + + for (i = 0; i < params; i++) anyNum[i] = (short)(param[i].numValue/one6); + for (i = 0; i < this->generators; i++) this->gen[i]->Call(params-1,anyNum,anyNum[params-1]); + break; + } + case dStroke: { + bool leftStationary[2]; + short cvt = (short)((params <= 6) ? invalidCvtNum : param[6].numValue/one6); + short actualCvt; + short knot[4]; + wchar_t buf[32]; + + for (i = 0; i < 2; i++) leftStationary[i] = param[i].numValue > 0; + for (i = 0; i < 4; i++) knot[i] = (short)(param[2+i].numValue/one6); + for (i = 0; i < this->generators; i++) { + this->gen[i]->DStroke(leftStationary,knot,cvt,&actualCvt,errMsg,errMsgLen); + if (actualCvt != cvt) { + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L",%hi",actualCvt); this->Insert(this->paramPos[6],buf); + } + } + break; + } + case fixDStrokes: + for (i = 0; i < this->generators; i++) this->gen[i]->FixDStrokes(); + break; + case grabHereInX: + for (i = 0; i < this->generators; i++) this->gen[i]->GrabHereInX((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),errMsg,errMsgLen); + break; + case intersect: { + short ppem0 = params > 5 ? (short)(param[5].numValue/one6) : noPpemLimit,ppem1 = params > 6 ? (short)(param[6].numValue/one6) : noPpemLimit; + + for (i = 0; i < this->generators; i++) this->gen[i]->Intersect((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),(short)(param[2].numValue/one6),(short)(param[3].numValue/one6),(short)(param[4].numValue/one6),ppem0,ppem1,errMsg,errMsgLen); + break; + } + case iStroke: { + bool leftStationary[2]; + short knot[4],height[2],phase,actualCvt; + short cvt = (short)((params <= 9) ? invalidCvtNum : param[9].numValue/one6); + wchar_t buf[32]; + + for (i = 0; i < 2; i++) leftStationary[i] = param[i].numValue > 0; + for (i = 0; i < 4; i++) knot[i] = (short)(param[2+i].numValue/one6); + for (i = 0; i < 2; i++) height[i] = (short)(param[6+i].numValue/one6); + phase = (short)(param[8].numValue/one6); + for (i = 0; i < this->generators; i++) { + this->gen[i]->IStroke(leftStationary,knot,height,phase,cvt,&actualCvt,errMsg,errMsgLen); + if (actualCvt != cvt) { + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L",%hi",actualCvt); this->Insert(this->paramPos[9],buf); + } + } + break; + } + case ::mainStrokeAngle: + for (i = 0; i < this->generators; i++) this->gen[i]->MainStrokeAngle((short)(param[0].numValue/one6),errMsg,errMsgLen); + break; + case ::glyphStrokeAngle: + for (i = 0; i < this->generators; i++) this->gen[i]->GlyphStrokeAngle((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),errMsg,errMsgLen); + break; + case quit: + for (i = 0; i < this->generators; i++) this->gen[i]->Quit(); + break; + case scoop: + for (i = 0; i < this->generators; i++) this->gen[i]->Scoop((short)(param[0].numValue/one6),(short)(param[1].numValue/one6),(short)(param[2].numValue/one6),errMsg,errMsgLen); + break; + case serif: { + bool forward = param[0].numValue > 0; + short type = (short)(param[1].numValue/one6),knots,knot[7]; + + knots = params-2; // direction flag and type + for (i = 0; i < knots; i++) knot[i] = (short)(param[2+i].numValue/one6); + for (i = 0; i < this->generators; i++) this->gen[i]->Serif(forward,type,knots,knot,errMsg,errMsgLen); + break; + } + case setItalicStrokeAngle: + for (i = 0; i < this->generators; i++) this->gen[i]->SetItalicStroke(false,errMsg,errMsgLen); + break; + case setItalicStrokePhase: + for (i = 0; i < this->generators; i++) this->gen[i]->SetItalicStroke(true,errMsg,errMsgLen); + break; + case smooth: + case xSmooth: + case ySmooth: { + short y = cmd == ySmooth ? 1 : (cmd == xSmooth ? 0 : -1); + short italicFlag = -1; + if (!this->legacyCompile) + { + bool haveFlag = params > 0 && param[0].type == angleFlag; + italicFlag = haveFlag ? (short) param[0].numValue : -1; + } + for (i = 0; i < this->generators; i++) this->gen[i]->Smooth(y,italicFlag); + break; + } + case diagonalMT: + case xDiagonal: + case yDiagonal: + case stroke: + case xStroke: + case yStroke: { + bool leftStationary[2],nearVert,nearHorz; + short base = cmd != xStroke && cmd != yStroke ? 2 : 0,optionalBase = base + 4, knot[4]; + short cvt = (short)((params <= optionalBase || param[optionalBase].type == ppemSize) ? invalidCvtNum : param[optionalBase].numValue/one6); + short ppem = (short)((params <= optionalBase || param[params-1].type != ppemSize) ? -1 : param[params-1].numValue/one6); + FVOverride fvOverride = cmd == stroke ? fvOldMethod : (cmd == xDiagonal || cmd == xStroke ? fvForceX : (cmd == yDiagonal || cmd == yStroke ? fvForceY : fvStandard)); + short actualCvt; + Vector leftEdge,rightEdge; + wchar_t buf[32]; + + for (i = 0; i < 4; i++) knot[i] = (short)((short)(param[base+i].numValue)/one6); + leftEdge.x = this->glyph->x[knot[2]] - this->glyph->x[knot[0]]; leftEdge.x = Abs(leftEdge.x); + leftEdge.y = this->glyph->y[knot[2]] - this->glyph->y[knot[0]]; leftEdge.y = Abs(leftEdge.y); + rightEdge.x = this->glyph->x[knot[3]] - this->glyph->x[knot[1]]; rightEdge.x = Abs(rightEdge.x); + rightEdge.y = this->glyph->y[knot[3]] - this->glyph->y[knot[1]]; rightEdge.y = Abs(rightEdge.y); + nearVert = leftEdge.x <= leftEdge.y*this->tanStraightAngle && rightEdge.x <= rightEdge.y*this->tanStraightAngle; + nearHorz = leftEdge.y <= leftEdge.x*this->tanStraightAngle && rightEdge.y <= rightEdge.x*this->tanStraightAngle; + if (cmd == xStroke && !nearVert) + swprintf(errMsg,errMsgLen,L"cannot accept XSTROKE (edges differ from vertical axis by %f degrees or more)",(double)STRAIGHTANGLEFUDGE); + else if (cmd == yStroke && !nearHorz) + swprintf(errMsg,errMsgLen,L"cannot accept YSTROKE (edges differ from horizontal axis by %f degrees or more)",(double)STRAIGHTANGLEFUDGE); + else if ((cmd == stroke || cmd == xStroke || cmd == yStroke) && (nearHorz || nearVert)) { // either nearHorz or nearVert, hence "informative" command + for (i = 0; i < 4; i += 2) for (j = 0; j < 4; j += 2) this->RegisterPartner(knot[i],knot[j+1],nearHorz,false,cvt); + } else { // diagonalMT, xDiagonal, yDiagonal, or general stroke "action" command + for (i = 0; i < 2; i++) leftStationary[i] = param[i].numValue > 0; + for (i = 0; i < this->generators; i++) { + this->gen[i]->Stroke(fvOverride,leftStationary,knot,cvt,ppem,&actualCvt,errMsg,errMsgLen); + if (actualCvt != cvt) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L",%hi", actualCvt); + this->Insert(this->paramPos[6], buf); + } + } + } + break; + } + case xStem: + case yStem: + case xRound: + case yRound: { + short knot[2],cvt; + + cvt = params > 2 ? (short)param[2].numValue/one6 : invalidCvtNum; + for (i = 0; i < 2; i++) knot[i] = (short)(param[i].numValue/one6); + + this->RegisterPartner(knot[0],knot[1],cmd == yStem || cmd == yRound,cmd == xRound || cmd == yRound,cvt); + break; + } + case vacuFormLimit: + for (i = 0; i < this->generators; i++) this->gen[i]->VacuFormLimit((short)(param[0].numValue/one6)); + break; + case vacuFormRound: { + short knot[4]; + bool forward[2]; + + for (i = 0; i < 2; i++) forward[i] = param[i].numValue > 0; + for (i = 0; i < 4; i++) knot[i] = (short)(param[4+i].numValue/one6); + for (i = 0; i < this->generators; i++) this->gen[i]->VacuFormRound((short)(param[2].numValue/one6),(short)(param[3].numValue/one6),forward,knot,errMsg,errMsgLen); + break; + } + case height: + this->RegisterHeight((short)(param[0].numValue/one6),(short)(param[1].numValue/one6)); + break; + case xAnchor: + case yAnchor: { + bool y = cmd == yAnchor,haveFlag = param[0].type == angleFlag; + ActParam *knotParam = ¶m[haveFlag]; + short knot = (short)(knotParam->numValue/one6); + short cvt = (short)((params <= haveFlag + 1) ? invalidCvtNum : param[haveFlag + 1].numValue/one6); + Height *height = y ? this->TheHeight(knot) : NULL; + short cvtHint = height ? height->cvtOverride : invalidCvtNum; + ProjFreeVector projFreeVector; + + if (cvt >= 0 && cvtHint >= 0) + swprintf(errMsg,errMsgLen,L"cannot override a cvt number specified by a HEIGHT command"); + else { + if (cvt < 0) cvt = cvtHint; // no cvt override => try previously specified cvt + // since MDAP, MIAP don't ever use the dual projection vector, we'll define the knot to be a child, hence it gets to override the fv + // this may change once I implement overrides for italic and adjusted italic angles, and this may turn out to be somewhat tricky: + // if we measure perpendicular to the italic angle, and move in x, then subsequent instructions on the same knot must move along the italic angle, and measure in y + // in other words, XAnchor/(knot) overrides the pv, while YAnchor/(knot) overrides the fv. + if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,NULL,knotParam,1,&projFreeVector,errMsg,errMsgLen)) { + for (i = 0; i < this->generators; i++) this->gen[i]->Anchor(cmd == yAnchor,&projFreeVector,knot,cvt,true,errMsg,errMsgLen); + } + } + break; + } + case xLink: + case yLink: + case xDist: + case yDist: { + bool y = cmd == yLink || cmd == yDist, + dist = cmd == xDist || cmd == yDist, + haveFlag = param[0].type == angleFlag, + havePostRound = param[haveFlag].type == postRoundFlag; + short base = haveFlag+havePostRound+2; + bool haveCvt = params > base && param[base].type == cvtN, + haveMinDist = params > base+haveCvt && param[base+haveCvt].type == minDistGeneral,relative; + ActParam *parentParam = ¶m[haveFlag+havePostRound],*childParam = ¶m[haveFlag+havePostRound+1]; + short parent = (short)(parentParam->numValue/one6), + child = (short)(childParam->numValue/one6), + cvt = haveCvt ? (short)(param[base].numValue/one6) : invalidCvtNum,actualCvt = cvt, + minDists = haveMinDist ? param[base+haveCvt].minDists : -1, + jumpPpemSize[maxMinDist]; + int32_t *jSize = minDists >= 0 ? ¶m[base+haveCvt].jumpPpemSize[0] : NULL; + Partner *partner = this->ThePartner(y,parent,child); + CvtCategory cvtCategory = dist ? cvtAnyCategory : (partner ? partner->category : cvtDistance); + short cvtHint = partner ? partner->cvtOverride : invalidCvtNum; + short lsb = this->knots - PHANTOMPOINTS,rsb = lsb + 1; + CharGroup charGroup; + LinkColor linkColor; + LinkDirection linkDirection; + wchar_t buf[32]; + ProjFreeVector projFreeVector; + + if (cvt >= 0 && cvtHint >= 0) + swprintf(errMsg,errMsgLen,L"cannot override a cvt number specified by an X|YSTROKE, an X|YSTEM, or an X|YROUND command"); + else if (dist && (cvtHint >= 0 || cvtCategory != cvtAnyCategory)) + swprintf(errMsg,errMsgLen,L"cannot use a X|YDIST command preceeded by X|YSTROKE, X|YSTEM, or X|YROUND"); + else if (havePostRound && !haveFlag) + swprintf(errMsg,errMsgLen,L"cannot use $ (post round flag) without using / (italic angle) or // (adjusted italic angle)"); + else { + if (!dist) { + if (cvt < 0) cvt = cvtHint; // no cvt override => try previously specified cvt + if (cvt >= 0) { + this->font->TheCvt()->GetCvtAttributes(cvt,&charGroup,&linkColor,&linkDirection,&cvtCategory,&relative); // fetch actual cvt category to encourage its use in GUI... + if (cvtCategory == cvtAnyCategory) cvtCategory = cvtDistance; // but default to cvtDistance if cvtAnyCategory to allow bi-directionality with GUI input of cvtAnyCategory as cvtDistance... + } else { + if (parent == lsb || child == lsb) cvtCategory = cvtLsb; + else if (parent == rsb || child == rsb) cvtCategory = cvtRsb; + // links lsb to rsb and v.v. not allowed by TTGenerator + } + } + + if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parentParam,childParam,1,&projFreeVector,errMsg,errMsgLen)) { + for (i = 0; i < minDists; i++) jumpPpemSize[i] = (short)(jSize[i]/one6); + for (i = 0; i < this->generators; i++) { + this->gen[i]->Link(y,dist,&projFreeVector,havePostRound,parent,child,cvtCategory,cvt,minDists,jumpPpemSize,param[base+haveCvt].pixelSize,&actualCvt,errMsg,errMsgLen); + if (actualCvt != cvt) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L",%hi", actualCvt); + this->Insert(this->paramPos[base], buf); + } + } + } + } + break; + } + case xMove: + case yMove: { + F26Dot6 amount = param[0].numValue; + short knots = params-1,knot[maxParams]; + + for (i = 0; i < knots; i++) knot[i] = (short)(param[1+i].numValue/one6); + for (i = 0; i < this->generators; i++) this->gen[i]->Move(cmd == yMove,amount,knots,knot,errMsg,errMsgLen); + break; + } + case xBDelta: + case xCDelta: + case xDelta: + case xGDelta: + case yBDelta: + case yDelta: + case yCDelta: + case yGDelta: { + short knot = (short)(param[0].numValue/one6),j; + DeltaColor cmdColor = cmd == xDelta || cmd == yDelta ? alwaysDelta : (cmd == xBDelta || cmd == yBDelta ? blackDelta : (cmd == xGDelta || cmd == yGDelta ? greyDelta : ctNatVerRGBIAWBLYDelta)),paramColor; + + + for (j = 1; j < params; j++) { + paramColor = param[j].deltaColor; + if (cmdColor != alwaysDelta && paramColor != alwaysDelta){ + swprintf(errMsg,errMsgLen,L"cannot override delta color specified by an X|YBDELTA or an X|YGDELTA or an X|YCDELTA command"); + }else{ + if (paramColor == alwaysDelta) paramColor = cmdColor; + for (i = 0; i < this->generators; i++) + this->gen[i]->Delta(cmd >= yBDelta,paramColor,knot,param[j].numValue,param[j].deltaPpemSize,errMsg,errMsgLen); + } + } + break; + } + case xHalfGrid: + case yHalfGrid: + case xDoubleGrid: + case yDoubleGrid: + case xDownToGrid: + case yDownToGrid: + case xUpToGrid: + case yUpToGrid: + case xNoRound: + case yNoRound: { + short knot[maxParams]; + Rounding r = rnone; + + for (i = 0; i < params; i++) knot[i] = (short)(param[i].numValue/one6); + switch (cmd) { + case xHalfGrid: + case yHalfGrid: r = rthg; break; + case xDoubleGrid: + case yDoubleGrid: r = rtdg; break; + case xDownToGrid: + case yDownToGrid: r = rdtg; break; + case xUpToGrid: + case yUpToGrid: r = rutg; break; + case xNoRound: + case yNoRound: r = roff; break; + default: break; + } + for (i = 0; i < this->generators; i++) this->gen[i]->SetRounding(cmd == yHalfGrid || cmd == yDoubleGrid || cmd == yDownToGrid || cmd == yUpToGrid || cmd == yNoRound,r,params,knot); + break; + } + case xInterpolate: // xInterpolate0, xInterpolate1 mapped to xInterpolate + case yInterpolate: // yInterpolate0, yInterpolate1 mapped to yInterpolate + case xIPAnchor: + case yIPAnchor: { + bool y = cmd == yInterpolate || cmd == yIPAnchor, + haveFlag = param[0].type == angleFlag, + havePostRound = param[haveFlag].type == postRoundFlag; + ActParam *parent0Param = ¶m[haveFlag+havePostRound], *childParam = ¶m[haveFlag+havePostRound+1]; + short parent0 = (short)(parent0Param->numValue/one6),parent1 = (short)(param[params-1].numValue/one6),children,child[maxParams]; + ProjFreeVector projFreeVector; + + if (havePostRound && !haveFlag) + swprintf(errMsg,errMsgLen,L"cannot use $ (post round flag) without using / (italic angle) or // (adjusted italic angle)"); + else { + children = params-2-havePostRound-haveFlag; // 2 parents... + if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parent0Param,childParam,children,&projFreeVector,errMsg,errMsgLen)) { + for (i = 0; i < children; i++) child[i] = (short)(childParam[i].numValue/one6); + for (i = 0; i < this->generators; i++) this->gen[i]->Interpolate(y,&projFreeVector,havePostRound,parent0,children,child,parent1,cmd == xIPAnchor || cmd == yIPAnchor,errMsg,errMsgLen); + } + } + break; + } + case xShift: + case yShift: { + bool y = cmd == yShift, + haveFlag = param[0].type == angleFlag; + ActParam *parentParam = ¶m[haveFlag], *childParam = ¶m[haveFlag+1]; + short parent = (short)(param[haveFlag].numValue/one6),children,child[maxParams]; + ProjFreeVector projFreeVector; + + children = params-1-haveFlag; + if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parentParam,childParam,children,&projFreeVector,errMsg,errMsgLen)) { + for (i = 0; i < children; i++) child[i] = (short)(childParam[i].numValue/one6); + for (i = 0; i < this->generators; i++) this->gen[i]->Shift(y,&projFreeVector,parent,children,child,errMsg,errMsgLen); + } + break; + } + // new rendering environment specific (Res) commands + case beginCodePath: + if (!this->legacyCompile) + { + short fpgmBias = (short)(param[0].numValue/one6); + + for (i = 0; i < this->generators; i++) this->gen[i]->BeginCodePath(fpgmBias,errMsg,errMsgLen); + } + break; + case endCodePath: + if (!this->legacyCompile) + { + for (i = 0; i < this->generators; i++) this->gen[i]->EndCodePath(errMsg,errMsgLen); + } + break; + case resXAnchor: + case resYAnchor: + if (!this->legacyCompile) + { + bool y = cmd == resYAnchor, + haveFlag = param[0].type == angleFlag; + short optParamOffs = haveFlag+1; + bool haveCvt = params > optParamOffs && param[optParamOffs].type == cvtN; + ActParam *childParam = ¶m[haveFlag]; + short child = (short)(childParam->numValue/one6), + cvt = haveCvt ? (short)(param[optParamOffs].numValue/one6) : invalidCvtNum; + ProjFreeVector projFreeVector; + + if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,NULL,childParam,1,&projFreeVector,errMsg, errMsgLen)) { + for (i = 0; i < this->generators; i++) this->gen[i]->ResAnchor(cmd == resYAnchor,&projFreeVector,child,cvt,errMsg,errMsgLen); + } + } + break; + case resXIPAnchor: + case resYIPAnchor: + if (!this->legacyCompile) + { + bool y = cmd == resYIPAnchor, + haveFlag = param[0].type == angleFlag, + havePostRound = param[haveFlag].type == postRoundFlag; + ActParam *parent0Param = ¶m[haveFlag+havePostRound], *childParam = ¶m[haveFlag+havePostRound+1]; + // ActParam *parent1Param = ¶m[haveFlag+havePostRound+2]; + short parent0 = (short)(parent0Param->numValue/one6),child = (short)(childParam->numValue/one6),parent1 = (short)(param[params-1].numValue/one6); + ProjFreeVector projFreeVector; + + if (havePostRound && !haveFlag) + swprintf(errMsg,errMsgLen,L"cannot use $ (post round flag) without using / (italic angle) or // (adjusted italic angle)"); + else { + if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parent0Param,childParam,1,&projFreeVector,errMsg,errMsgLen)) { + for (i = 0; i < this->generators; i++) this->gen[i]->ResIPAnchor(y,&projFreeVector,havePostRound,parent0,child,parent1,errMsg,errMsgLen); + } + } + } + break; + case resXDist: + case resYDist: + case resXLink: + case resYLink: + if (!this->legacyCompile) + { + bool y = cmd == resYDist || cmd == resYLink, + dist = cmd == resXDist || cmd == resYDist, + haveFlag = param[0].type == angleFlag; + short optParamOffs = haveFlag+2; + bool haveCvt = params > optParamOffs && param[optParamOffs].type == cvtN, + haveMinDistFlag = params > optParamOffs+haveCvt && param[optParamOffs+haveCvt].type == minDistFlagOnly; + short minDists = haveMinDistFlag ? param[optParamOffs+haveCvt].minDists : -1; + ActParam *parentParam = ¶m[haveFlag],*childParam = ¶m[haveFlag+1]; + short parent = (short)(parentParam->numValue/one6), + child = (short)(childParam->numValue/one6), + cvt = haveCvt ? (short)(param[optParamOffs].numValue/one6) : invalidCvtNum; + ProjFreeVector projFreeVector; + + if (this->MakeProjFreeVector(haveFlag,param[0].numValue,y,parentParam,childParam,1,&projFreeVector,errMsg,errMsgLen)) { + for (i = 0; i < this->generators; i++) { + this->gen[i]->ResLink(y,dist,&projFreeVector,parent,child,cvt,minDists,errMsg,errMsgLen); + } + } + } + break; + case resXIPDist: + case resYIPDist: + case resXIPLink: + case resYIPLink: + if (!this->legacyCompile) + { + bool y = cmd == resYIPDist || cmd == resYIPLink, + dist = cmd == resXIPDist || cmd == resYIPDist, + haveAngleFlag = param[0].type == angleFlag, + haveStrokeFlag = param[haveAngleFlag].type == strokeFlag; + ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], + *parentParam = ¶m[haveAngleFlag+haveStrokeFlag+1], + *childParam = ¶m[haveAngleFlag+haveStrokeFlag+2], + *cvtParam = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+3] : NULL, + *grandParent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+3]; + short strokeFlag = haveStrokeFlag ? (short)param[haveAngleFlag].numValue : 0, + grandParent0 = (short)(grandParent0Param->numValue/one6), + parent = (short)(parentParam->numValue/one6), + child = (short)(childParam->numValue/one6), + cvt = !dist ? (short)(cvtParam->numValue/one6) : invalidCvtNum, + grandParent1 = (short)(grandParent1Param->numValue/one6); + ProjFreeVector projFreeVector; + + if (this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,grandParent0Param,parentParam,2,&projFreeVector,errMsg,errMsgLen)) { // 2 children (?) + for (i = 0; i < this->generators; i++) { + this->gen[i]->ResIPLink(y,dist,&projFreeVector,strokeFlag,grandParent0,parent,child,cvt,grandParent1,errMsg,errMsgLen); + } + } + } + break; + case resXIPDDist: + case resYIPDDist: + case resXIPDLink: + case resYIPDLink: + if (!this->legacyCompile) + { + bool y = cmd == resYIPDDist || cmd == resYIPDLink, + dist = cmd == resXIPDDist || cmd == resYIPDDist, + haveAngleFlag = false, // so far, TT fn not implemented orthogonally enough + haveStrokeFlag = false; // so far, TT fn not implemented orthogonally enough + //short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, + // cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; + ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], + *parent0Param = ¶m[haveAngleFlag+haveStrokeFlag+1], + *child0Param = ¶m[haveAngleFlag+haveStrokeFlag+2], + *cvt0Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+3] : NULL, + *parent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+3], + *child1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+4], + *cvt1Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+!dist+5] : NULL, + *grandParent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+!dist+5]; + short strokeFlag = haveStrokeFlag ? (short)param[haveAngleFlag].numValue : 0, + grandParent0 = (short)(grandParent0Param->numValue/one6), + parent0 = (short)(parent0Param->numValue/one6), + child0 = (short)(child0Param->numValue/one6), + cvt0 = !dist ? (short)(cvt0Param->numValue/one6) : invalidCvtNum, + parent1 = (short)(parent1Param->numValue/one6), + child1 = (short)(child1Param->numValue/one6), + cvt1 = !dist ? (short)(cvt1Param->numValue/one6) : invalidCvtNum, + grandParent1 = (short)(grandParent1Param->numValue/one6); + ProjFreeVector projFreeVector; + + if (this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,grandParent0Param,parent0Param,4,&projFreeVector,errMsg,errMsgLen)) { // 4 children (?) + for (i = 0; i < this->generators; i++) { + this->gen[i]->ResIPDLink(y,dist,&projFreeVector,strokeFlag,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1,errMsg,errMsgLen); + } + } + } + break; + case resXIPDDDist: + case resYIPDDDist: + case resXIPDDLink: + case resYIPDDLink: + if (!this->legacyCompile) + { + bool y = cmd == resYIPDDDist || cmd == resYIPDDLink, + dist = cmd == resXIPDDDist || cmd == resYIPDDDist, + haveAngleFlag = param[0].type == angleFlag, + haveStrokeFlag = false; // so far, TT fn not implemented orthogonally enough + // short cvt0ParamOffs = haveAngleFlag+haveStrokeFlag+3, + // cvt1ParamOffs = haveAngleFlag+haveStrokeFlag+6; + ActParam *grandParent0Param = ¶m[haveAngleFlag+haveStrokeFlag], + *parent0Param = ¶m[haveAngleFlag+haveStrokeFlag+1], + *child0Param = ¶m[haveAngleFlag+haveStrokeFlag+2], + *cvt0Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+3] : NULL, + *parent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+3], + *child1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+4], + *cvt1Param = !dist ? ¶m[haveAngleFlag+haveStrokeFlag+!dist+5] : NULL, + *grandParent1Param = ¶m[haveAngleFlag+haveStrokeFlag+!dist+!dist+5]; + short strokeFlag = haveStrokeFlag ? (short)param[haveAngleFlag].numValue : 0, + grandParent0 = (short)(grandParent0Param->numValue/one6), + parent0 = (short)(parent0Param->numValue/one6), + child0 = (short)(child0Param->numValue/one6), + cvt0 = !dist ? (short)(cvt0Param->numValue/one6) : invalidCvtNum, + parent1 = (short)(parent1Param->numValue/one6), + child1 = (short)(child1Param->numValue/one6), + cvt1 = !dist ? (short)(cvt1Param->numValue/one6) : invalidCvtNum, + grandParent1 = (short)(grandParent1Param->numValue/one6); + ProjFreeVector projFreeVector0,projFreeVector1; + + if (this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,parent0Param,child0Param,1,&projFreeVector0,errMsg,errMsgLen) && + this->MakeProjFreeVector(haveAngleFlag,param[0].numValue,y,parent1Param,child1Param,1,&projFreeVector1,errMsg,errMsgLen)) { + // pv not allowed to override, only fv, hence copy into single projFreeVector parameter + projFreeVector0.fv[1] = projFreeVector1.fv[0]; + for (i = 0; i < this->generators; i++) { + this->gen[i]->ResIPDDLink(y,dist,&projFreeVector0,strokeFlag,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1,errMsg,errMsgLen); + } + } + } + break; + case resXDDist: + case resYDDist: + case resXDLink: + case resYDLink: + if (!this->legacyCompile) + { + bool y = cmd == resYDDist || cmd == resYDLink, + dist = cmd == resXDDist || cmd == resYDDist; + short cvtParamOffs0 = 2; + bool haveCvt0 = !dist && params > cvtParamOffs0 && param[cvtParamOffs0].type == cvtN; + short cvtParamOffs1 = 2 + haveCvt0 + 2; + bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN; + // bool haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; + ActParam *parent0Param = ¶m[0],*child0Param = ¶m[1],*parent1Param = ¶m[2+haveCvt0],*child1Param = ¶m[3+haveCvt0]; + short parent0 = (short)(parent0Param->numValue/one6), + child0 = (short)(child0Param->numValue/one6), + cvt0 = haveCvt0 ? (short)(param[cvtParamOffs0].numValue/one6) : invalidCvtNum, + parent1 = (short)(parent1Param->numValue/one6), + child1 = (short)(child1Param->numValue/one6), + cvt1 = haveCvt1 ? (short)(param[cvtParamOffs1].numValue/one6) : cvt0; + ProjFreeVector projFreeVector0,projFreeVector1; + + if (this->MakeProjFreeVector(false,0,y,parent0Param,child0Param,1,&projFreeVector0,errMsg,errMsgLen) && + this->MakeProjFreeVector(false,0,y,parent1Param,child1Param,1,&projFreeVector1,errMsg,errMsgLen)) { + // pv not allowed to override, only fv, hence copy into single projFreeVector parameter + projFreeVector0.fv[1] = projFreeVector1.fv[0]; + for (i = 0; i < this->generators; i++) { + this->gen[i]->ResDDLink(y,dist,&projFreeVector0,parent0,child0,cvt0,parent1,child1,cvt1,errMsg,errMsgLen); + } + } + } + break; + case resIIPDDist: + case resIIPDLink: + if (!this->legacyCompile) + { + bool dist = cmd == resIIPDDist; + // short optParamOffs = cmd == resIIPDDist ? 6 : 8; + + short cvtParamOffs0 = 3; + bool haveCvt0 = !dist && params > cvtParamOffs0 && param[cvtParamOffs0].type == cvtN; + short cvtParamOffs1 = 3 + haveCvt0 + 2; + bool haveCvt1 = !dist && params > cvtParamOffs1 && param[cvtParamOffs1].type == cvtN; + // bool haveMinDist = params > cvtParamOffs1+haveCvt1 && param[cvtParamOffs1+haveCvt1].type == minDistGeneral; + ActParam *grandParent0Param = ¶m[0],*parent0Param = ¶m[1],*child0Param = ¶m[2],*parent1Param = ¶m[3+haveCvt0],*child1Param = ¶m[4+haveCvt0],*grandParent1Param = ¶m[5+haveCvt0+haveCvt1]; + short grandParent0 = (short)(grandParent0Param->numValue/one6), + parent0 = (short)(parent0Param->numValue/one6), + child0 = (short)(child0Param->numValue/one6), + cvt0 = haveCvt0 ? (short)(param[cvtParamOffs0].numValue/one6) : invalidCvtNum, + parent1 = (short)(parent1Param->numValue/one6), + child1 = (short)(child1Param->numValue/one6), + grandParent1 = (short)(grandParent1Param->numValue/one6), + cvt1 = haveCvt1 ? (short)(param[cvtParamOffs1].numValue/one6) : cvt0; + ProjFreeVector projFreeVector0,projFreeVector1; + + if (this->MakeProjFreeVector(true,0,false,parent0Param,child0Param,1,&projFreeVector0,errMsg,errMsgLen) && + this->MakeProjFreeVector(true,0,false,parent1Param,child1Param,1,&projFreeVector1,errMsg,errMsgLen)) { + // pv not allowed to override, only fv, hence copy into single projFreeVector parameter + projFreeVector0.fv[1] = projFreeVector1.fv[0]; + for (i = 0; i < this->generators; i++) { + this->gen[i]->ResIIPDLink(dist,&projFreeVector0,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1,errMsg,errMsgLen); + } + } + } + break; + case compilerLogic: + case cvtAllocation: + case cvtLogic: + case diagEndCtrl: + case diagSerifs: + case processXSymmetrically: + case processYSymmetrically: + case tail: + case tweakMetrics: + default: + swprintf(errMsg,errMsgLen,L"Sorry, this command is no longer supported"); + break; + } +} /* TMTSourceParser::Dispatch */ + +void TMTSourceParser::XFormToNewSyntax(void) { +/* this is a bit of a botched job, but I'd rather have the standard parameter checking mechanism do all the serious work */ + int32_t savePos,flagPos,parmPos = 0; + wchar_t old[32],neu[64]; + short s,d,l; + + savePos = this->pos; + + while (this->ch && this->ch != L'(' && this->ch != L'[') this->GetCh(); // scan for start of parameter list + flagPos = this->pos-chLookAhead; + this->GetCh(); + while (this->ch && this->ch != L')' && this->ch != L']' && this->ch != L'"') { // find opening quote + if (this->ch == L',') parmPos = this->pos-chLookAhead; + this->GetCh(); + } + l = 0; + if (this->ch == L'"') { + this->GetCh(); + while (this->ch && this->ch != L'"' && l < 31) { old[l++] = this->ch; this->GetCh(); } // find closing quote + old[l] = '\0'; this->GetCh(); + } + + if (l > 0) { + this->talkText->Delete(parmPos,this->pos-chLookAhead); + + d = s = 0; + while (s < l) { + if (old[s] == L'/') { // italic angle + neu[d++] = old[s]; + old[s++] = L' '; + } else if (old[s] == 0xAF) { // adjusted italic angle + neu[d++] = L'/'; + neu[d++] = L'/'; + old[s++] = L' '; + } else if (old[s] == 0xA8) { // post round + neu[d++] = L'$'; + old[s++] = L' '; + } else { + s++; + } + } + if (d > 0) { + neu[d] = '\0'; + this->talkText->Insert(flagPos,neu); + parmPos += d; + } + + d = s = 0; + while (s < l-2) { + if ((old[s] == L'c' || old[s] == L'C') && (old[s+1] == L'v' || old[s+1] == L'V') && (old[s+2] == L't' || old[s+2] == L'T')) { // "cvt123" + neu[d++] = L','; + old[s++] = L' '; old[s++] = L' '; old[s++] = L' '; + while (s < l && ((L'0' <= old[s] && old[s] <= L'9') || old[s] == L'.' || old[s] == L' ')) { + if (old[s] != L' ') neu[d++] = old[s]; + old[s++] = L' '; + } + } else { + s++; + } + } + s = 0; + while (s < l) { + if (old[s] == L'<' || old[s] == 0xB3) { // "<" or "≥12" or "≥(12,@2,24)" + neu[d++] = L','; + if (old[s] == L'<') + neu[d++] = old[s]; + else { + neu[d++] = L'>'; + neu[d++] = L'='; + } + old[s++] = L' '; + if (s < l && old[s] == L'(') { // "≥(12,@2,24)" + neu[d++] = old[s]; old[s++] = L' '; // '(' + while (s < l-1 && old[s] != L',') { neu[d++] = old[s]; old[s++] = L' '; } + neu[d++] = old[s]; old[s++] = L' '; // ',' + neu[d++] = L'@'; + while (s < l-1 && old[s] != L')') { neu[d++] = old[s]; old[s++] = L' '; } + neu[d++] = old[s]; old[s++] = L' '; // ')' + } else { // "≥12" + while (s < l && ((L'0' <= old[s] && old[s] <= L'9') || old[s] == L'.')) { neu[d++] = old[s]; old[s++] = L' '; } + } + } else { + s++; + } + } + if (d > 0) { + neu[d] = '\0'; + this->talkText->Insert(parmPos,neu); + parmPos += d; + } + + d = s = 0; + neu[d++] = L','; + neu[d++] = L'"'; + while (s < l) { + if (old[s] != L' ') neu[d++] = old[s]; + s++; + } + neu[d++] = L'"'; + if (d > 3) { + neu[d] = '\0'; + this->talkText->Insert(parmPos,neu); + parmPos += d; + } + this->changedSrc = true; + } + + this->pos = savePos-chLookAhead; + this->GetCh(); this->GetCh(); +} /* TMTSourceParser::XFormToNewSyntax */ + +/*****/ +void TMTSourceParser::Flag(ActParam *actParam) { + int32_t paramStart; + + paramStart = this->prevPos; + switch (this->sym) { + case leftDir: + case rightDir: + actParam->type = dirFlag; actParam->numValue = (int32_t)this->sym - (int32_t)leftDir; + this->GetSym(); + break; + case italAngle: + case adjItalAngle: + actParam->type = angleFlag; actParam->numValue = (int32_t)this->sym - (int32_t)italAngle; + this->GetSym(); + break; + case optStroke: + case optStrokeLeftBias: + case optStrokeRightBias: + if (!this->legacyCompile) + { + actParam->type = strokeFlag; actParam->numValue = (int32_t)this->sym - (int32_t) optStroke + 1; + this->GetSym(); + } + break; + case postRound: + actParam->type = postRoundFlag; + this->GetSym(); + break; + default: + break; + } + this->prevPrevPos = paramStart; +} // TMTSourceParser::Flag + +/***** +void TMTSourceParser::Flag(ActParam *actParam) { + int32_t paramStart; +#ifdef VTT_PRO_SP_YAA_AUTO + bool doubleSlash; + ActParam fvPoint0,fvPoint1; + short subParams; +#endif + + paramStart = this->prevPos; + switch (this->sym) { + case leftDir: + case rightDir: + actParam->type = dirFlag; actParam->numValue = (int32_t)this->sym - (int32_t)leftDir; + this->GetSym(); + break; + case italAngle: + case adjItalAngle: +#ifdef VTT_PRO_SP_YAA_AUTO + doubleSlash = this->sym == adjItalAngle; +#endif + actParam->type = angleFlag; + actParam->numValue = (int32_t)this->sym - (int32_t)italAngle; + this->GetSym(); +#ifdef VTT_PRO_SP_YAA_AUTO + if (InitParam(this)) { + if (!doubleSlash) this->ErrorMsg(syntactical,L"Cannot use / to delimit freedom vector direction (use // instead)"); + subParams = 0; + this->Parameter(&fvPoint0); this->MatchParameter(fvPointsParam,&subParams,&fvPoint0.type); this->ValidateParameter(&fvPoint0); + if (Separator(this) || InitParam(this)) { + if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); + if (Separator(this)) this->GetSym(); + this->Parameter(&fvPoint1); this->MatchParameter(fvPointsParam,&subParams,&fvPoint1.type); this->ValidateParameter(&fvPoint1); + } else { + fvPoint1 = fvPoint0; + } + if (this->sym == adjItalAngle) this->GetSym(); else this->ErrorMsg(syntactical,L"// expected"); + actParam->fvPoint0 = (short)(fvPoint0.numValue/one6); + actParam->fvPoint1 = (short)(fvPoint1.numValue/one6); + } else { + actParam->fvPoint0 = actParam->fvPoint1 = invalidKnotNum; + } +#endif + break; + case postRound: + actParam->type = postRoundFlag; + this->GetSym(); + break; + } + this->prevPrevPos = paramStart; +} // TMTSourceParser::Flag +*****/ + +void TMTSourceParser::Parameter(ActParam *actParam) { + int32_t paramStart,localParamStart; + short subParams; + ActParam colorParam; + Symbol ttvSym; + ParamType paramType; + int32_t numValue,firstLocalParamStart = 0; + bool gotKnot[2]; + + paramStart = this->prevPos; + if (leftParen <= this->sym && this->sym <= rational) { + this->Expression(actParam); + this->prevPrevPos = paramStart; // can't recursively call this->Parameter() here... + actParam->hasTtvOverride = false; + actParam->ttvOverride.dir = xRomanDir; // say + actParam->ttvOverride.from = invalidKnotNum; + actParam->ttvOverride.to = invalidKnotNum; + + if (this->sym == aT) { + subParams = 0; + if (actParam->type == anyN) actParam->type = rationalN; // for now + this->MatchParameter(rangeOfPpemNParam,&subParams,&actParam->type); + this->ValidateParameter(actParam); + this->GetSym(); + this->PpemRange(actParam); + actParam->deltaColor = alwaysDelta; + if (this->sym == percent) { // optional delta color sub-parameter + this->GetSym(); + this->Parameter(&colorParam); + if (colorParam.type == anyN) colorParam.type = colorN; + subParams = 0; + this->MatchParameter(deltaColorParam,&subParams,&colorParam.type); + this->ValidateParameter(&colorParam); + actParam->deltaColor = DeltaColorOfByte((unsigned char)(colorParam.numValue/one6)); + actParam->type = rangeOfPpemNcolorOpt; // by now + } + } else if (!this->legacyCompile && (this->sym == colon || this->sym == rightDir || this->sym == upDir)) { + // the following are all valid: + // + // knot no ttv override + // knot > ttv in x-direction + // knot ^ ttv in y-direction + // knot > knot1 ttv on line from knot to knot1 + // knot ^ knot1 ttv perpendicular to line from knot to knot1 + // knot : knot0 > knot1 ttv on line from knot0 to knot1 + // knot : knot0 ^ knot1 ttv perpendicular to line from knot to knot1 + // + // (no italic or adjusted italic angle yet, + // this would probably complement the ^ and > by / or // ... + // or use \ and \\ for perpendicular to the italic angle and don't require ^ and > ???) + // + // at this point we have parsed knot, now we're about to parse the rest + + actParam->hasTtvOverride = true; + + subParams = 0; + paramType = actParam->type; + if (paramType == anyN) actParam->type = knotN; // for now + this->MatchParameter(knotNttvOptParams,&subParams,&actParam->type); this->ValidateParameter(actParam); + numValue = actParam->numValue; + + gotKnot[0] = false; + + if (this->sym == colon) { + this->GetSym(); + + firstLocalParamStart = localParamStart = this->prevPos; + this->Expression(actParam); + this->prevPrevPos = localParamStart; + this->MatchParameter(knotNttvOptParams,&subParams,&actParam->type); this->ValidateParameter(actParam); + if (actParam->type == knotN) actParam->ttvOverride.from = (short)(actParam->numValue/one6); + + gotKnot[0] = true; + } + + if (this->sym != rightDir && this->sym != upDir) + this->ErrorMsg(syntactical,L"> or ^ expected"); + else { + ttvSym = this->sym; + this->GetSym(); + } + + gotKnot[1] = false; + + if (this->sym != rightParen && !Separator(this)) { + + localParamStart = this->prevPos; + this->Expression(actParam); + this->prevPrevPos = localParamStart; + this->MatchParameter(knotNttvOptParams,&subParams,&actParam->type); this->ValidateParameter(actParam); + if (actParam->type == knotN) actParam->ttvOverride.to = (short)(actParam->numValue/one6); + + gotKnot[1] = true; + } + + if (gotKnot[0] && !gotKnot[1]) { + this->prevPrevPos = firstLocalParamStart; + this->ErrorMsg(contextual,L"invalid freedom or projection vector (second knot expected)"); + } else if (gotKnot[0] && gotKnot[1] && actParam->ttvOverride.from == actParam->ttvOverride.to) { + this->prevPrevPos = firstLocalParamStart; + this->ErrorMsg(contextual,L"invalid freedom or projection vector (knots must differ)"); + } + + // no italic or adjusted italic angle yet + actParam->ttvOverride.dir = gotKnot[1] ? (ttvSym == upDir ? perpDiagDir : diagDir) : (ttvSym == upDir ? yRomanDir : xRomanDir); + + if (paramType == anyN) actParam->type = gotKnot[0] || gotKnot[1] ? knotNttvOpt : knotNttvOptXY; // reset + actParam->numValue = numValue; + } + } else if (this->sym == aT) { + this->GetSym(); + localParamStart = this->prevPos; + this->Expression(actParam); + this->prevPrevPos = localParamStart; // can't recursively call this->Parameter() here... + if (actParam->type != anyN) { + this->ErrorMsg(contextual,L"ppem size expected (can be an integer only)"); + actParam->numValue = one6; + } + actParam->type = ppemSize; + } else if (this->sym == literal) { + actParam->type = anyS; actParam->litValue = this->litValue; + this->GetSym(); + } else if (this->sym == atLeast || this->sym == leftDir) { + this->MinDist(actParam); + } else { + this->ErrorMsg(syntactical,L"parameter starts with invalid symbol (+, -, @, <, >=, number, or \x22string\x22 expected)"); actParam->type = voidParam; actParam->numValue = 0; + } + this->prevPrevPos = paramStart; +} /* TMTSourceParser::Parameter */ + +bool Match(ParamType formParamType, ParamType actParamType); +bool Match(ParamType formParamType, ParamType actParamType) { + return (actParamType == formParamType || + (actParamType == anyN && anyN <= formParamType && formParamType <= posRationalN) || + // actParamType == knotNttvOpt && knotNttvOpt <= formParamType && formParamType <= k + (knotNttvOpt <= actParamType && actParamType <= knotNttvOptXY && knotNttvOpt <= formParamType && formParamType <= knotNttvOptXY) || + (actParamType == rangeOfPpemN && formParamType == rangeOfPpemNcolorOpt) || + (actParamType == posRationalN && rationalN <= formParamType && formParamType <= posRationalN) || + (actParamType == minDistFlagOnly && formParamType == minDistGeneral)); +} /* Match */ + +void TMTSourceParser::MatchParameter(FormParam *formParams, short *formParamNum, ParamType *actParamType) { + short tentative; + wchar_t errMsg[maxLineSize]; + ParamType expected; + + tentative = *formParamNum; + while (tentative < maxFPs && !Match(formParams[tentative].type,*actParamType) && opt <= formParams[tentative].pres && formParams[tentative].pres <= optR) tentative++; + if (*formParamNum < tentative && tentative < maxFPs && Match(formParams[tentative].type,*actParamType)) *formParamNum = tentative; // skip optional parameters + if (*formParamNum < maxFPs && formParams[*formParamNum].type != voidParam) { + if (Match(formParams[*formParamNum].type,*actParamType)) + *actParamType = formParams[*formParamNum].type; + else { + expected = *formParamNum < maxFPs ? formParams[*formParamNum].type : voidParam; + switch (expected) { + case anyN: + case knotN: + case knotNttvOpt: + case knotNttvOptXY: + case cvtN: + case compLogicN: + case cvtLogicN: + case phaseN: + case angle100N: + case colorN: + case serifN: + case curveN: + case radiusN: + swprintf(errMsg,sizeof(errMsg)/sizeof(wchar_t),L"integer number expected (example: 1)"); + break; + case rationalN: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"rational number expected (example: 1/8 or -1.5)"); + break; + case posRationalN: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"positive rational number expected (example: 1/8 or 1.5)"); + break; + case ppemSize: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"ppem size expected (example: @12)"); + break; + case ppemN: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"ppem number expected (example: 12)"); + break; + case rangeOfPpemNcolorOpt: + case rangeOfPpemN: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"ppem range expected (example: @8..13;21)"); + break; + case anyS: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"quoted string expected (example: \x22V1.11\x22 or %cCALL[], 9\x22)", '\x22'); + break; // %c or else compiler won't accept "escape sequence"... + case minDistFlagOnly: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"minimum distance flag expected (example: < or >= only)"); + break; + case minDistGeneral: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"minimum distance expected (example: < or >= or >=1.5 or >=(1.5,@12,2.5) )"); + break; + case dirFlag: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"direction flag expected (example: either < or >)"); + break; + case angleFlag: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"angle flag expected (example: either / or //)"); + break; + case postRoundFlag: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"post round flag expected (example: $)"); + break; + default: + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"actual parameter does not match"); + break; + } + this->ErrorMsg(contextual,errMsg); + } + if (mand <= formParams[*formParamNum].pres && formParams[*formParamNum].pres <= opt) (*formParamNum)++; // use next parameter next time; + } else { + this->ErrorMsg(contextual,L"too many parameters"); + } +} /* TMTSourceParser::MatchParameter */ + +void TMTSourceParser::ValidateParameter(ActParam *actParam) { + /* test if actParam->type is in range of phaseN or angle100N or whatever it is by now */ + wchar_t errMsg[maxLineSize]; + + switch (actParam->type) { // which by now is the formal parameter type + case voidParam: + break; + case anyN: + break; + case knotNttvOpt: + case knotN: { + int32_t knot = actParam->numValue/one6; + + if (knot < 0 || knot >= this->knots) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"invalid knot number (can be in range 0 through %hi only)", this->knots - 1); + this->ErrorMsg(contextual, errMsg); + actParam->numValue = 0; + } + // for knotNttvOpt, ttvKnot[0], and ttvKnot[1] already validated against being in range + // what's left to do is to verify (if we can) whether this yields an almost perpendicular case. + // probably can't do at this level, because we don't know the value of the projection vector. + // would need to know tt->PVDir() but this may not be valid if compiling into graphical representation + break; + } + case cvtN: { + int32_t cvt = actParam->numValue/one6; + + if (!this->font->TheCvt()->CvtNumExists(cvt)) { + this->ErrorMsg(contextual,L"invalid cvt number (must be defined in the control value table)"); + actParam->numValue = 0; + } + break; + } + case compLogicN: + break; + case cvtLogicN: + break; + case phaseN: + if (actParam->numValue < 0 || actParam->numValue >= phases*one6) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"invalid phase type (can be in range 0 through %li only)", phases - 1); + this->ErrorMsg(contextual, errMsg); + actParam->numValue = 0; + } + break; + case angle100N: + if (actParam->numValue < 0 || actParam->numValue > maxAngle*one6) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"invalid angle x100 (can be in range 0 through %li only)", maxAngle); + this->ErrorMsg(contextual, errMsg); + actParam->numValue = 0; + } + break; + case colorN: + if (DeltaColorOfByte((unsigned char)(actParam->numValue/one6)) == invalidDelta) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"invalid delta color flag (can be " NARROW_STR_FORMAT L" only)", AllDeltaColorBytes()); + this->ErrorMsg(contextual, errMsg); + actParam->numValue = 0; + } + break; + case serifN: + if (actParam->numValue < 0 || actParam->numValue >= serifs*one6) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"invalid serif type (can be in range 0 through %li only)", serifs - 1); + this->ErrorMsg(contextual, errMsg); + actParam->numValue = 0; + } + break; + case curveN: + break; + case radiusN: + break; + case rationalN: + case posRationalN: + if ((actParam->type == posRationalN && actParam->numValue < 0) || actParam->numValue < -maxPixelValue || actParam->numValue > maxPixelValue) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"invalid pixel size (can be in range %li through %li only)", actParam->type == posRationalN ? 0 : -maxPixelValue / one6, maxPixelValue / one6); + this->ErrorMsg(contextual, errMsg); + actParam->numValue = one6; + } + if (actParam->numValue == 0) { + this->ErrorMsg(contextual,L"pixel size cannot be 0"); + actParam->numValue = one6; + } + break; + case ppemSize: + case ppemN: + if (actParam->numValue < one6 || actParam->numValue >= maxPpemSize*one6) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"invalid ppem number (can be in range 1 through %li only)", maxPpemSize - 1); + this->ErrorMsg(contextual, errMsg); + actParam->numValue = one6; + } + break; + case rangeOfPpemN: + /* this->numValue already validated in PpemRange to be able to report errors at correct source position, likewise this->deltaPpemSize */ + break; + case anyS: + break; + case minDistGeneral: + /* this->jumpPpemSize[maxMinDist] and this->pixelSize[maxMinDist] already validated in MinDist for same reason as case deltaRangeN: */ + break; + case dirFlag: + break; + case angleFlag: + break; + case postRoundFlag: + break; + default: + break; + } +} /* TMTSourceParser::ValidateParameter */ + +void TMTSourceParser::Expression(ActParam *actParam) { + Symbol sign,op; + ActParam actParam2; + wchar_t errMsg[maxLineSize]; + + sign = plus; + if (this->sym == plus || this->sym == minus) { + sign = this->sym; this->GetSym(); + } + this->Term(actParam); + if (sign == minus) actParam->numValue = -actParam->numValue; + while (this->sym == plus || this->sym == minus) { + op = this->sym; this->GetSym(); + this->Term(&actParam2); + if (op == plus) actParam->numValue += actParam2.numValue; else actParam->numValue -= actParam2.numValue; // assuming we have not more than 32 - 17 - 1 binary places + if (Abs(actParam->numValue) >= (shortMax+1)*one6) { + if (op == plus) + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"result of addition too large (cannot be %li or above)", shortMax + 1); + else + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"result of subtraction too large (cannot be -%li or below)", shortMax + 1); + this->ErrorMsg(contextual,errMsg); + } + actParam->type = Max(actParam->type,actParam2.type); + } + if (actParam->type == rationalN && actParam->numValue >= 0) actParam->type = posRationalN; +} /* TMTSourceParser::Expression */ + +void TMTSourceParser::Term(ActParam *actParam) { + Symbol op; + ActParam actParam2; + wchar_t errMsg[maxLineSize]; + + this->Factor(actParam); + while (this->sym == timeS || this->sym == italAngle) { + op = this->sym; this->GetSym(); + this->Factor(&actParam2); + if (op == timeS) { + if ((double)Abs(actParam->numValue)*(double)Abs(actParam2.numValue) < (double)((shortMax+1)*one6*one6)) + actParam->numValue = (actParam->numValue*actParam2.numValue + half6)/one6; + else { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"result of multiplication too large (cannot be %li or larger in magnitude)", shortMax + 1); + this->ErrorMsg(contextual, errMsg); + } + } else { // op == italAngle, i.e. divide + if (actParam2.numValue != 0 && (double)Abs(actParam->numValue) < (double)(shortMax+1)*(double)Abs(actParam2.numValue)) { + if (actParam->type == anyN && actParam2.type == anyN && actParam->numValue%actParam2.numValue != 0) actParam->type = rationalN; + actParam->numValue = (2*actParam->numValue*one6 + actParam2.numValue)/(2*actParam2.numValue); + } else { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"result of division too large (cannot be %li or larger in magnitude)", shortMax + 1); + this->ErrorMsg(contextual, errMsg); + } + } + actParam->type = Max(actParam->type,actParam2.type); + } +} /* TMTSourceParser::Term */ + +void TMTSourceParser::Factor (ActParam *actParam) { + if (this->sym == natural || this->sym == rational) { + actParam->type = this->sym == natural ? anyN : rationalN; + actParam->numValue = this->numValue; this->GetSym(); + } else if (this->sym == leftParen) { + this->GetSym(); + this->Expression(actParam); + if (this->sym == rightParen) this->GetSym(); else this->ErrorMsg(syntactical,L") expected"); + } else { + this->ErrorMsg(syntactical,L"factor starts with invalid symbol (number or ( expected)"); + actParam->type = voidParam; + } +} /* TMTSourceParser::Factor */ + +void TMTSourceParser::MinDist(ActParam *actParam) { + Symbol op; + bool haveLeftParen,gotMinDist; + short subParams; + ActParam ppem,pixel; + wchar_t errMsg[maxLineSize]; + + op = this->sym; + actParam->type = minDistFlagOnly; + actParam->minDists = 0; + this->GetSym(); + if (op == atLeast) { + haveLeftParen = false; + if (this->sym == leftParen) { haveLeftParen = true; this->GetSym(); } + gotMinDist = InitParam(this); + subParams = 0; + if (gotMinDist) { this->Parameter(&pixel); this->MatchParameter(minDistParam,&subParams,&pixel.type); this->ValidateParameter(&pixel); } + else { pixel.type = anyN; pixel.numValue = one6; } + actParam->jumpPpemSize[actParam->minDists] = one6; // say... + actParam->pixelSize[actParam->minDists] = pixel.numValue; + actParam->minDists++; + if (haveLeftParen && gotMinDist) { + while (Separator(this) || InitParam(this)) { + if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); + if (Separator(this)) this->GetSym(); + this->Parameter(&ppem); this->MatchParameter(minDistParam,&subParams,&ppem.type); this->ValidateParameter(&ppem); + if (ppem.numValue <= actParam->jumpPpemSize[actParam->minDists-1]) { + this->ErrorMsg(contextual,L"this ppem size should be larger than the previous one"); + ppem.numValue = actParam->jumpPpemSize[actParam->minDists-1] + one6; + } + if (Separator(this) || InitParam(this)) { + if (this->sym != comma) this->ErrorMsg(syntactical,L", expected"); + if (Separator(this)) this->GetSym(); + this->Parameter(&pixel); this->MatchParameter(minDistParam,&subParams,&pixel.type); this->ValidateParameter(&pixel); + if (pixel.numValue <= actParam->pixelSize[actParam->minDists-1]) { + this->ErrorMsg(contextual,L"this pixel size should be larger than the previous one"); + pixel.numValue = actParam->pixelSize[actParam->minDists-1] + one6; + } + } else { + this->ErrorMsg(syntactical,L", (followed by another pixel size) expected"); + } + if (actParam->minDists < maxMinDist) { + actParam->jumpPpemSize[actParam->minDists] = ppem.numValue; + actParam->pixelSize[actParam->minDists] = pixel.numValue; + actParam->minDists++; + } else { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"too many minimum distances (cannot have more than %li)", maxMinDist); + this->ErrorMsg(contextual, errMsg); + } + } + } + if (haveLeftParen) { + if (this->sym == rightParen) this->GetSym(); else this->ErrorMsg(syntactical,L") expected"); + } + if (gotMinDist || actParam->minDists > 1 || actParam->pixelSize[0] != one6 || actParam->jumpPpemSize[0] != one6) { + actParam->type = minDistGeneral; + } + } +} /* TMTSourceParser::MinDist */ + +void TMTSourceParser::Range(ActParam *actParam) { + ActParam lowParam,highParam; + short subParams,low,high,i; + wchar_t errMsg[maxLineSize]; + + subParams = 1; // skip pixel number + this->Parameter(&lowParam); + if (lowParam.type == anyN) lowParam.type = ppemN; + this->MatchParameter(rangeOfPpemNParam,&subParams,&lowParam.type); + this->ValidateParameter(&lowParam); + low = high = (short)(lowParam.numValue/one6); + if (this->sym == ellipsis) { + this->GetSym(); + this->Parameter(&highParam); + if (highParam.type == anyN) highParam.type = ppemN; + this->MatchParameter(rangeOfPpemNParam,&subParams,&highParam.type); + this->ValidateParameter(&highParam); + high = (short)(highParam.numValue/one6); + if (low > high) { this->ErrorMsg(contextual,L"low end of ppem range cannot be above high end"); high = low; } + } + for (i = low; i <= high; i++) { + if (!actParam->deltaPpemSize[i]) + actParam->deltaPpemSize[i] = true; + else { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"ppem size %hi occurs more than once", i); + this->ErrorMsg(contextual,errMsg); + } + } +} /* TMTSourceParser::Range */ + +void TMTSourceParser::PpemRange(ActParam *actParam) { + /* e.g. XDelta(13, 3/8 @ 12..18; 20; 24, 2/8 @ 19; 21..23) */ + short i; + + actParam->type = rangeOfPpemN; // by now + for (i = 0; i < maxPpemSize; i++) actParam->deltaPpemSize[i] = false; + this->Range(actParam); + while (this->sym == semiColon || InitParam(this)) { + if (this->sym == semiColon) this->GetSym(); else this->ErrorMsg(syntactical,L"; expected"); + this->Range(actParam); + } +} /* TMTSourceParser::PpemRange */ + +void TMTSourceParser::GetCh(void) { + this->ch = this->ch2; + this->ch2 = this->talkText->GetCh(this->pos); + this->pos++; +} /* TMTSourceParser::GetCh */ + +void TMTSourceParser::SkipComment(void) { + int32_t startPos; + + startPos = this->pos-chLookAhead; + this->GetCh(); this->GetCh(); + while (this->ch && !TermComment(this)) { + if (InitComment(this)) this->SkipComment(); else this->GetCh(); + } + if (this->ch) { + this->GetCh(); this->GetCh(); + } else { + this->prevPos = startPos; + this->ErrorMsg(special,L"comment opened but not closed"); + } +} /* TMTSourceParser::SkipComment */ + +void TMTSourceParser::SkipWhiteSpace(bool includingComments) { + while (WhiteSpace(this) || (includingComments && InitComment(this))) { + if (WhiteSpace(this)) this->GetCh(); + if (includingComments && InitComment(this)) this->SkipComment(); + } +} /* TMTSourceParser::Skip */ + +void TMTSourceParser::GetNumber(void) { + bool overflow; + int32_t digit,decPlcs,pwrOf10; + wchar_t errMsg[maxLineSize]; + + this->sym = natural; + overflow = false; + this->numValue = 0; + while (Numeric(this->ch) || Alpha(this->ch) || this->ch == L'_') { + if (Numeric(this->ch)) { + digit = (int32_t)this->ch - (int32_t)'0'; + if (this->numValue <= (shortMax - digit)/10) + this->numValue = 10*this->numValue + digit; + else + overflow = true; + } else + this->ErrorMsg(lexical,L"invalid character in number (can be digits 0 through 9 only)"); + this->GetCh(); + } + this->numValue *= one6; + if (this->ch == L'.' && this->ch2 != L'.') { // permit ppem ranges such as 12..18 + this->GetCh(); + this->sym = rational; + decPlcs = 0; pwrOf10 = 1; + while (Numeric(this->ch) || Alpha(this->ch) || this->ch == L'_') { + if (Numeric(this->ch)) { + digit = (int32_t)this->ch - (int32_t)'0'; + if (decPlcs <= (1000000L - digit)/10) { // 1/64 = 0.015625 + decPlcs = 10*decPlcs + digit; pwrOf10 *= 10L; + } else + overflow = true; + } else + this->ErrorMsg(lexical,L"invalid character in number (can be digits 0 through 9 only)"); + this->GetCh(); + } + this->numValue += (decPlcs*one6 + pwrOf10/2)/pwrOf10; + } + if (overflow) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"number too large (cannot be %li or larger in magnitude)", shortMax + 1); + this->ErrorMsg(syntactical, errMsg); + } +} /* TMTSourceParser::GetNumber */ + +Symbol Search(wchar_t *entry, short left, short right, short *matching); +Symbol Search(wchar_t *entry, short left, short right, short *matching) { + short mid,diff = 0,minMatch; + wchar_t *id,*en; + + while (left <= right) { + mid = (left + right)/2; + id = tmtCmd[mid].name; minMatch = tmtCmd[mid].minMatch; *matching = 0; en = entry; + while (*matching < minMatch && !(diff = Cap(*id) - Cap(*en))) {(*matching)++; id++; en++;} + if (diff > 0) right = mid - 1; + else if (diff < 0) left = mid + 1; + else return (Symbol)mid; // found + } + return invalid; // not found +} /* Search */ + +void TMTSourceParser::GetIdent(void) { + wchar_t id[idLen],*replId; + short i,matching,origLen,replLen; + bool textReplace; + + i = origLen = 0; + while (Alpha(this->ch) || Numeric(this->ch) || this->ch == L'_') { + if (this->ch != L'_' && i < idLen-1) id[i++] = this->ch; + origLen++; this->GetCh(); + } + id[i] = '\0'; + this->sym = Search(id,0,idents-1,&matching); + if (this->sym != invalid) { + replId = tmtCmd[this->sym].name; + replLen = (short)STRLENW(replId); + for (matching = 0; matching < replLen && id[matching] == replId[matching]; matching++); + textReplace = matching < replLen || i > replLen; // original contains extra characters + if (this->sym == xInterpolate0 || this->sym == xInterpolate1) { this->sym = xInterpolate; replLen = (short)STRLENW(tmtCmd[this->sym].name); textReplace = true; } else + if (this->sym == yInterpolate0 || this->sym == yInterpolate1) { this->sym = yInterpolate; replLen = (short)STRLENW(tmtCmd[this->sym].name); textReplace = true; } + if (textReplace) this->ReplAtCurrPos(origLen,tmtCmd[this->sym].name); + } +} /* TMTSourceParser::GetIdent */ + +void TMTSourceParser::GetLiteral(void) { + bool overflow; + short i; + wchar_t errMsg[maxLineSize]; + + this->sym = literal; + this->GetCh(); + overflow = false; + i = 0; + while (this->ch && this->ch != L'"') { + if (i < maxAsmSize-1) + this->litValue[i++] = this->ch; + else + overflow = true; + this->GetCh(); + } + this->litValue[i++] = '\0'; + + if (!this->ch) this->ErrorMsg(special,L"string quoted but not unquoted"); + this->GetCh(); + if (overflow) { + swprintf(errMsg, sizeof(errMsg) / sizeof(wchar_t), L"string too long (cannot be longer than %li characters)", maxAsmSize - 1); + this->ErrorMsg(syntactical, errMsg); + } +} + +void TMTSourceParser::GetSym(void) { + this->SkipWhiteSpace(true); + this->prevPrevPos = this->prevPos; this->prevPos = this->pos-chLookAhead; + if (Numeric(this->ch)) { + this->GetNumber(); + } else if (Alpha(this->ch)) { + this->GetIdent(); + } else { + switch (this->ch) { + case L'(': this->sym = leftParen; this->GetCh(); break; + case L'[': this->sym = leftParen; this->GetCh(); this->ReplAtCurrPos(1,L"("); break; + case L')': this->sym = rightParen; this->GetCh(); break; + case L']': this->sym = rightParen; this->GetCh(); this->ReplAtCurrPos(1,L")"); break; + + case 0xB3: this->sym = atLeast; this->GetCh(); this->ReplAtCurrPos(1,L">="); break; // replace Mac special char + case L'+': this->sym = plus; this->GetCh(); break; + case L'-': this->sym = minus; this->GetCh(); break; + case L'*': this->sym = timeS; this->GetCh(); break; + case L':': this->sym = colon; this->GetCh(); break; + case L'%': this->sym = percent; this->GetCh(); break; + case L',': this->sym = comma; this->GetCh(); break; + case L';': this->sym = semiColon; this->GetCh(); break; + case L'@': this->sym = aT; this->GetCh(); break; + case L'<': + this->GetCh(); + if (this->ch == L'|') { + this->sym = optStrokeLeftBias; + this->GetCh(); + this->ReplAtCurrPos(2,L"|<"); + } else { + this->sym = leftDir; + } + break; + case L'^': this->sym = upDir; this->GetCh(); break; + case L'>': + this->GetCh(); + if (this->ch == L'=') { + this->sym = atLeast; + this->GetCh(); + } else if (this->ch == L'|') { + this->sym = optStrokeRightBias; + this->GetCh(); + } else { + this->sym = rightDir; + } + break; + case L'/': + this->GetCh(); + if (this->ch == L'/') { + this->sym = adjItalAngle; + this->GetCh(); + } else { + this->sym = italAngle; + } + break; + case L'|': + this->GetCh(); + if (this->ch == L'|') { + this->sym = optStroke; + this->GetCh(); + } else if (this->ch == L'<') { + this->sym = optStrokeLeftBias; + this->GetCh(); + } else if (this->ch == L'>') { + this->sym = optStrokeRightBias; + this->GetCh(); + this->ReplAtCurrPos(2,L">|"); + } else { + this->sym = invalid; + } + break; + case 0xAF: this->sym = adjItalAngle; this->GetCh(); this->ReplAtCurrPos(1,L"//"); break; // replace Mac special char + case L'$': this->sym = postRound; this->GetCh(); break; + case 0xA8: this->sym = postRound; this->GetCh(); this->ReplAtCurrPos(1,L"$"); break; // replace Mac special char + case L'"': this->GetLiteral(); break; + case L'.': + this->GetCh(); + if (this->ch == L'.') { + this->sym = ellipsis; + this->GetCh(); + } else { + this->sym = period; + } + break; + case '\x0': this->sym = eot; break; + default: + this->ErrorMsg(lexical,L"unknown character"); + break; + } + } +} /* TMTSourceParser::GetSym */ + +void TMTSourceParser::Delete(int32_t pos, int32_t len) { + if (len > 0) { + this->talkText->Delete(pos,pos + len); + if (this->pos > pos + len) this->pos -= len; + else if (this->pos > pos) this->pos = pos; + this->changedSrc = true; + } +} /* TMTSourceParser::Delete */ + +void TMTSourceParser::Insert(int32_t pos, const wchar_t strg[]) { + int32_t len; + + len = (int32_t)STRLENW(strg); + if (len > 0) { + this->talkText->Insert(pos,strg); + if (this->pos > pos) this->pos += len; + this->changedSrc = true; + } +} /* TMTSourceParser::Insert */ + +void TMTSourceParser::ReplAtCurrPos(short origLen, const wchar_t repl[]) { + int32_t pos; + + pos = this->pos-chLookAhead-origLen; + this->Delete(pos,origLen); + this->Insert(pos,repl); +} /* TMTSourceParser::ReplAtCurrPos */ + +void TMTSourceParser::ErrorMsg(short kind, const wchar_t errMsg[]) { + if (this->errPos < 0) { // no error reported yet + this->ch = this->ch2 = L'\x0'; + this->sym = eot; + if (errMsg[0] == L'\0') + this->errPos = 0; // dummy error to QUIT() compilation... + else { + switch (kind) { + case special: this->errPos = this->pos; this->symLen = this->errPos - this->prevPos; break; + case lexical: this->errPos = this->pos-chLookAhead+1; this->symLen = 1; break; + case syntactical: this->errPos = this->pos-chLookAhead; this->symLen = this->errPos - this->prevPos; break; + case contextual: this->errPos = this->prevPos; this->symLen = this->errPos - this->prevPrevPos; break; + } + STRCPYW(this->errMsg,errMsg); + } + } +} /* TMTSourceParser::ErrorMsg */ + +TMTParser *NewTMTSourceParser(void) { + return new TMTSourceParser; +} + +bool TMTCompile(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TextBuffer *glyfText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen) { + TTEngine *ttengine; // should think of keeping these around somewhere w/o having to allocate over and over again... + short generators,i; + TTGenerator *ttgenerator[3]; + TMTParser *tmtparser; + bool memError,changedSrc; + + if (glyph->componentSize > 0) return true; // don't touch composites... + + tmtparser = NewTMTSourceParser(); memError = !tmtparser; + generators = 0; + if (glyfText) { + ttgenerator[generators] = NewTTSourceGenerator(); + if (!ttgenerator[generators] ) memError = true; else generators++; + } +/***** + if (grafWindow) { + ttgenerator[generators] = NewTTGlyphStrGenerator(); + if (!ttgenerator[generators] ) memError = true; else generators++; + } +*****/ + if (glyfText) { + ttengine = NewTTSourceEngine(); + memError = memError || !ttengine; + } else + ttengine = NULL; + if (ttengine && !memError) ttengine->InitTTEngine(legacyCompile, &memError); + for (i = 0; i < generators && !memError; i++) ttgenerator[i]->InitTTGenerator(font,glyph,glyphIndex,ttengine,legacyCompile,&memError); + if (!memError) { + tmtparser->InitTMTParser(talkText,font,glyph,legacyCompile,generators,ttgenerator); + tmtparser->Parse(&changedSrc,errPos,errLen,errMsg,errMsgLen); + tmtparser->TermTMTParser(); + for (i = 0; i < generators; i++) ttgenerator[i]->TermTTGenerator(); + if (ttengine) ttengine->TermTTEngine(*errPos < 0 ? glyfText : NULL,&memError); + } + if (ttengine) delete ttengine; + for (i = 0; i < generators; i++) delete ttgenerator[i]; + if (tmtparser) delete tmtparser; + + if (memError) + swprintf(errMsg, errMsgLen, L"Insufficient memory for compilation"); + else if (*errPos > 0) *errPos -= *errLen; + return !memError && *errPos + *errLen <= 0; +} /* TMTCompile */ + +#if _DEBUG +bool TMTRemoveAltCodePath(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen) { + TMTParser *tmtparser; + bool memError,changedSrc; + + if (glyph->componentSize > 0) return true; // don't touch composites... + + tmtparser = NewTMTSourceParser(); memError = !tmtparser; + if (!memError) { + tmtparser->InitTMTParser(talkText,font,glyph,false, 0,NULL); + tmtparser->RemoveAltCodePath(&changedSrc,errPos,errLen,errMsg,errMsgLen); + tmtparser->TermTMTParser(); + } + if (tmtparser) delete tmtparser; + + if (memError) swprintf(errMsg,errMsgLen,L"Insufficient memory for compilation"); + else if (*errPos > 0) *errPos -= *errLen; + return !memError && *errPos + *errLen <= 0; +} // TMTRemoveAltCodePath +#endif + + +/* the flickering at the end of the compilation comes from a flag (edit_saveOnCharChange) + that forces every change to be saved (with the ominous double fs_DoFile...) and subse- + quently reloaded, which in turn causes an update event for all visible windows (at least + the main-, tmt-, and tt-window), and apparently, doing a TEInsert causes an update of + the respective window as well, hence the tt-window gets updated twice (once before the + save). + For the future, might think of making such things more consistent: remember what + has changed, or that something has changed, and save changes only upon request (alerting + the uncautious user of unsaved data upon close). Should poll the users what they want, + explaining the pros (speed, flickering) and cons (computer failure induced loss of data). + + looking at all the /one6 statements, it might turn out to be more sensible to come back + with these values the way they have to be, such as in ValidateParam. + + Another question raised (by Claude): wouldn't it be possible to check, whether an in- + formative command does preceed the action command(s) it is supposed to influence. I think + with the GUI to come, and its data structure, this will come for free, so I'd rather not + duplicate work now. + + An idea in this area is to get rid of having to use X|YRound, X|YStroke, but instead make + the compiler/code generator more intelligent: if there is no cvt override, and if there + are no informative commands upon generating code for a Link, then try to obtain the fea- + ture category (cvtRound, cvtStroke, cvtDistance) automatically. Should be fairly simple for + rounds, may have to extract some code from the auto-hinter for determining the strokes. + + Seems like the values for the overshoots are actually relative to the caps line (etc.), + ending up doing much like what I do: actualHeight = capsLine + overshoot + + Why are there different values for the cvt_cut_in <27, 27-68, >68 ppem??? + + In any case the X|YStroke had probably better be called X|YStem and expect 2 knots only, + rather than 4, much like the X|YRound, or an even number of parameters? + +*/ diff --git a/src/TMTParser.h b/src/TMTParser.h index 0791acb..2b94ed4 100644 --- a/src/TMTParser.h +++ b/src/TMTParser.h @@ -1,26 +1,26 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef TMTParser_dot_h -#define TMTParser_dot_h - -class TMTParser { -public: - virtual void Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[]); -#if _DEBUG - virtual void RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[]); -#endif - virtual void InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]); - virtual void TermTMTParser(void); - TMTParser(void); - virtual ~TMTParser(void); -}; - -TMTParser *NewTMTSourceParser(void); -bool TMTCompile(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TextBuffer *glyfText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]); // returns true if compilation completed successfully - -#if _DEBUG -bool TMTRemoveAltCodePath(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t *errPos, int32_t *errLen, wchar_t errMsg[]); -#endif - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef TMTParser_dot_h +#define TMTParser_dot_h + +class TMTParser { +public: + virtual void Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen); +#if _DEBUG + virtual void RemoveAltCodePath(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t error[], size_t errorLen); +#endif + virtual void InitTMTParser(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, bool legacyCompile, short generators, TTGenerator *gen[]); + virtual void TermTMTParser(void); + TMTParser(void); + virtual ~TMTParser(void); +}; + +TMTParser *NewTMTSourceParser(void); +bool TMTCompile(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TextBuffer *glyfText, bool legacyCompile, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen); // returns true if compilation completed successfully + +#if _DEBUG +bool TMTRemoveAltCodePath(TextBuffer *talkText, TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen); +#endif + #endif // TMTParser_dot_h \ No newline at end of file diff --git a/src/TTAssembler.cpp b/src/TTAssembler.cpp index 61d77cc..9139576 100644 --- a/src/TTAssembler.cpp +++ b/src/TTAssembler.cpp @@ -1,4111 +1,4110 @@ -/***** - * - * TTAssembler.c - * - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - * - * - * Notes on TrueType Assembler GregH 12/2011 - * Instruction matching is a linear search. Should be easy to move to a binary search - * Numbers can be hexadecimal when prefixed by a 0x (not 0X). Hex digits can be upper or lower case - * Maximum number is 0xFFFF if positive or 0x8FFF if negative - * + or - can prefix numbers - * 256 (PUSH_ARG) is the maximum number of arguments for one #PUSH - * A label must be less than 22 (MAXLABELLENGTH) characters - * 200 (MAXJRPAIR) total PUSHs in a block - * - *****/ -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include /* for swprintf */ -#include /* for wcslen */ -#include /* for SHRT_MIN MAX */ -#include "pch.h" - -#define SHORTMAX 32767 -#define SHORTMIN -32768 -#define USHORTMAX 65535 - -#define MAXINSTRUCTIONCOUNT 16000 -#define MAXARGUMENTS (0x8000L - 4L) // don't push the limit -#define ARGTYPEBUFFER_SIZE 300 - -/* maximumembeding of #BEGIN #END block */ -#define MAXBLOCKEMBEDDING 6 - -/* ClaudeBe for the DELTA command, DeltaBase is constant 9 and DeltaShift is constant 3, - use the DLT command with relative ppem (0..15) and relative offset (-8..8) if you need - a specific DeltaBase or DeltaShift */ -#define DELTABASE 9 - -#define MAXIFRECURSION 20 - -/* maximal number of delta in one command */ -#define MAXDELTA 256 - -/* for the DovMan partial compilation feature that flash points referenced by the current command */ -#define tt_MAXFLASHINGPOINTS 4 - -#define tt_MAXINSTRUCTIONS 8000 /* size needed for the chicago Japanese fonts */ - -typedef struct { - unsigned short flashingPoints[tt_MAXFLASHINGPOINTS]; - short NumberOfFlashingPoint; -} tt_flashingPoints; - -#define tt_NoError 0 -#define tt_EmbeddedComment 1 -#define tt_UnterminatedComment 2 -#define tt_UnknownSwitch 3 -#define tt_UnknownInstruction 4 -#define tt_TwoInstructionsInSameLine 5 /* could mean garbage, too many arguments,... */ -#define tt_BooleanFlagsMissing 6 -#define tt_WrongNumberOfBoolean 7 -#define tt_TooManyBooleans 8 -#define tt_UnrecognizedBoolean 9 -#define tt_MissingClosingBracket 10 -#define tt_SLOOPArgumentBufferTooSmall 11 /* reduce the count for SLOOP, the compiler cannot handle such a big number */ -#define tt_EmptyParameterList 12 /* missing comma between parameters or empty parameter list */ -#define tt_UnableToParseArgument 13 -#define tt_MissingParameters 14 -#define tt_PointNbOutOfRange 15 -#define tt_CVTIndexOutOfRange 16 -#define tt_StorageIndexOutOfRange 17 -#define tt_ContourNbOutOfRange 18 -#define tt_FunctionNbOutOfRange 19 -#define tt_ArgumentOutOfRange 20 -#define tt_ArgumentIndexOutOfRange 21 /* this error should never happend, this comes from a programming error */ -#define tt_NotEnoughMemory 22 -#define tt_DeltaListMissing 23 -#define tt_DeltaOpeningParenthesisMissing 24 -#define tt_PointSizeOutOfRange 25 -#define tt_DeltaDenominatorMissing 26 -#define tt_DeltaWrongDenominator 27 -#define tt_DeltaAtSignMissing 28 -#define tt_TooManyDeltas 29 -#define tt_DeltaClosingBracketMissing 30 -#define tt_DeltaOORangePpem 31 -#define tt_TooManyLabels 32 -#define tt_LabelTooLong 33 -#define tt_DuplicateLabel 34 -#define tt_EndWithoutBegin 35 -#define tt_MissingEndBlock 36 -#define tt_TooManyEnbeddedBlocks 37 -#define tt_CompositeCode 38 -#define tt_VoidLabel 39 -#define tt_LabelNotFound 40 -#define tt_ExpectingAComma 41 -#define tt_TooManyPushArgs 42 -#define tt_ParseOverflow 44 -#define tt_JRExpectingABracket 45 -#define tt_JRExpectingABWLabel 46 -#define tt_JRExpectingAEqual 47 -#define tt_JRExpectingALabel 48 - -#define tt_JumpTooBigForByte 49 - -#define tt_EIFwithoutIF 50 -#define tt_ELSEwithoutIF 51 -#define tt_ELSEwithinELSE 52 -#define tt_TooManyEmbeddedIF 53 -#define tt_ExpectingaBEGIN 54 - -#define tt_FDEFInsideFDEF 55 -#define tt_ENDFwithoutFDEF 56 - -#define tt_IFwithoutEIF 57 -#define tt_FDEFwithoutENDF 58 -#define tt_IDEFwithoutENDF 59 - -#define tt_PUSHONwhenAlreadyOn 60 -#define tt_PUSHOFFwhenAlreadyOff 61 -#define tt_IFgoingAcrossBlocks 62 -#define tt_FDEFgoingAcrossBlocks 63 -#define tt_IDEFgoingAcrossBlocks 64 - -#define tt_FDEFInsideIDEF 65 -#define tt_IDEFInsideFDEF 66 -#define tt_IDEFInsideIDEF 67 -#define tt_IDEF_FDEFinGlyphProgram 68 -#define tt_INSTCTRLnotInPreProgram 69 -#define tt_ProgramTooBig 70 -#define tt_TooManyArguments 71 -#define tt_DELTAWithoutArguments 72 - -#define tt_DeltaClosingParenthesisMissing 73 - -#define tt_DELTAWithArguments 74 - -#define tt_PUSHBWInPushON 75 -#define tt_WildCardInPush 76 -#define tt_JumpNegativeForByte 77 - -#define tt_NotImplemented 9999 -#define tt_Push_Switch 1 -#define tt_PushOn_Switch 2 -#define tt_PushOff_Switch 3 -#define tt_Begin_Switch 4 -#define tt_End_Switch 5 -#define tt_GHTBlockBegin_Switch 6 -#define tt_GHTBlockEnd_Switch 7 - -#define co_NoError 0 - -/* start at 100 for the composite errors to avoid overlapping */ - -#define co_TwoInstructionsInSameLine 101 -#define co_BooleanFlagsMissing 102 -#define co_UnrecognizedBoolean 103 -#define co_WrongNumberOfBoolean 104 -#define co_MissingClosingBracket 105 -#define co_TooManyBooleans 106 - -#define co_EmptyParameterList 107 -#define co_PointNbOutOfRange 108 -#define co_GlyphIndexOutOfRange 109 -#define co_ArgumentOutOfRange 110 -#define co_MissingParameters 111 - -#define co_2_14Overflow 114 - -#define co_NotImplemented 9999 - -typedef struct { - short WeAreInsideAnIF; - short NumberOfEmbeddedIF; /* for #PUSHON mode */ - short WeAreInsideAnFDEF; - short WeAreInsideAnIDEF; - short WeAreInPushOnMode; - short WeAreInPrePushMode; - short WeAreInsideGHBlock; - short LastCommandWasAnIF; /* used to check for #BEGIN (beginning of a block) */ - short LastCommandWasAnELSE; /* used to check for #BEGIN (beginning of a block) */ - short LastCommandWasAnFDEF; /* used to check for #BEGIN (beginning of a block) */ - short LastCommandWasAnIDEF; /* used to check for #BEGIN (beginning of a block) */ - short LastCommandWasAnJUMP; /* used to check for #BEGIN (beginning of a block) */ - short LastCommandWasAnEND; /* to check if the ENDF, ELSE or EIF is preceeded by #END (end of block) */ - short ELSEStatus[MAXIFRECURSION]; /* to keep track in embedded IF statement - if we are already in the ELSE clause */ -} tt_CompilationStatus; - - -void TT_memSwap( char *a,char *b,char *tmp,int32_t len ); -void TT_memSwap( char *a,char *b,char *tmp,int32_t len ) -{ - memcpy( tmp, a, len ); - memcpy( a, b, len ); - memcpy( b, tmp, len ); -} - - -typedef struct { - unsigned short point; - unsigned short relppem; - short num; /* numerator */ - unsigned short denum; /* denominator */ -} tt_deltaPType; - - -typedef struct { - const wchar_t *name; /* compiler switch string */ - const wchar_t *description; /* descriptive info */ - short index; /* switch index */ -} tt_CompilerSwitchType; - - -typedef struct { - wchar_t type; - wchar_t code; - wchar_t result; -} asm_BooleanTranslationType; - -typedef struct { - wchar_t type; - wchar_t Reserved1; /* for struct alignment */ - wchar_t Reserved2; - wchar_t Reserved3; - const wchar_t *explanation; - short lowestValidValue; /* 6-7-90 JTS Range Checking Adds */ - short highestValidValue; /* 6-7-90 JTS Range Checking Adds */ -} asm_PushAndPopDescriptionType; - -/* lower case is reserved for loop variable dependent pops */ -asm_PushAndPopDescriptionType asm_ppDescription1[] = { - { L'P',L' ',L' ',L' ', L"Point Number" ,0 ,0}, - { L'E',L' ',L' ',L' ', L"Zone Pointer" ,0 ,0}, /* This is handled differently */ - { L'D',L' ',L' ',L' ', L"Distance in pixels, I pixel = 64 units" ,SHORTMIN ,SHORTMAX}, - { L'B',L' ',L' ',L' ', L"bool, true or false" ,SHORTMIN ,SHORTMAX}, - { L'R',L' ',L' ',L' ', L"Vector Projection" ,-16384 ,16384}, - { L'F',L' ',L' ',L' ', L"Function Number" ,0 ,255}, - { L'I',L' ',L' ',L' ', L"Index into control value table" ,0 ,0}, - { L'J',L' ',L' ',L' ', L"Index into storage area" ,0 ,0}, - { L'*',L' ',L' ',L' ', L"Anything" ,SHORTMIN ,SHORTMAX}, - { L'&',L' ',L' ',L' ', L"Entire Stack" ,SHORTMIN ,SHORTMAX}, - { L'C',L' ',L' ',L' ', L"Contour Number" ,0 ,0}, - { L'N', L' ',L' ',L' ', L"Small Number" ,0 ,255}, - { L'V',L' ',L' ',L' ', L"Positive Value" ,0 ,SHORTMAX}, /* New values added to use RangeChecking*/ - { L'L', L' ',L' ',L' ', L"Label" ,SHORTMIN ,SHORTMAX}, /* new ClaudeBe, to parse the label of a JR instruction */ - { L'A', L' ',L' ',L' ', L"Unsigned Byte" ,0 ,255}, /* NPUSH, number of puses */ - { L'H', L' ',L' ',L' ', L"Signed Word" ,SHORTMIN ,SHORTMAX} /* PUSHW argument */ -}; /* See typedef asm_PushAndPopDescriptionType (above) 6-7-90 JTS Range Checking Adds */ - -#define NumberOfPPEntries 15 - -tt_CompilerSwitchType tt_CompilerSwitch[] = { -/**** Instruction name, descriptive info, index *****/ - { L"PUSH", L"Push arguments on the stack", tt_Push_Switch }, - { L"PUSHON", L"Set direct push mode on", tt_PushOn_Switch }, - { L"PUSHOFF", L"Set direct push mode off", tt_PushOff_Switch }, - { L"BEGIN", L"Beginning of a block", tt_Begin_Switch }, - { L"END", L"End of a block", tt_End_Switch }, - { L"GHTBLOCK", L"Beginning of a new Sampo block", tt_GHTBlockBegin_Switch }, - { L"GHTB", L"Beginning of a new Sampo block", tt_GHTBlockBegin_Switch }, - { L"GHTE", L"End of a new Sampo block", tt_GHTBlockEnd_Switch } -}; - -#define asm_SLOOP 0x17 - -typedef struct { - const wchar_t *name; /* Apple instruction name */ - const wchar_t *description; /* descriptive info */ - const wchar_t *pops; /* What the instruction pops */ - const wchar_t *pushes; /* What the instruction pushes */ - unsigned short baseCode; /* base code, need to be short because of the fake code and the duplicate DELTA */ - const wchar_t *booleans; /* booleans */ -} tt_InstructionType; - -/****** - array defining the TrueType instructions, it look like : -tt_InstructionType tt_instruction[] = { -/+ Instruction name, descriptive info, pops, pushes, base code, booleans -/ - { "SVTCA", L"Set Vectors To Coordinate Axis", L"", L"", 0x00, "A" }, -....... - -*****/ - -#define tt_TOTALNUMBEROFINSTRUCTIONS 178 - -const tt_InstructionType tt_instruction[] = { -/**** Instruction name, descriptive info, pops, pushes, base code, booleans *****/ - { L"SVTCA", L"Set Vectors To Coordinate Axis", L"", L"", 0x00, L"A"}, - { L"SPVTCA", L"Set Projection Vector To Coordinate Axis", L"", L"", 0x02, L"A"}, - { L"SFVTCA", L"Set Freedom Vector To To Cordinate Axis", L"", L"", 0x04, L"A"}, - { L"SPVTL", L"Set Projection Vector To Line", L"PP", L"", 0x06, L"R"}, - { L"SFVTL", L"Set Freedom Vector To Line", L"PP", L"", 0x08, L"R"}, - { L"SPVFS", L"Set Projection Vector From Stack", L"RR", L"", 0x0A, L"" }, - { L"SFVFS", L"Set Freedom Vector From Stack", L"RR", L"", 0x0B, L"" }, - { L"GPV", L"Get Projection Vector", L"", L"RR", 0x0C, L"" }, - { L"GFV", L"Get Freedom Vector", L"", L"RR", 0x0D, L"" }, - { L"SFVTPV", L"Set Freedom Vector To Projection Vector", L"", L"", 0x0E, L"" }, - { L"ISECT", L"InterSECT", L"PPPPP", L"", 0x0F, L"" }, - { L"SRP0", L"Set Reference Point 0", L"P", L"", 0x10, L"" }, - { L"SRP1", L"Set Reference Point 1", L"P", L"", 0x11, L"" }, - { L"SRP2", L"Set Reference Point 2", L"P", L"", 0x12, L"" }, - { L"SZP0", L"Set Zone Pointer 0", L"E", L"", 0x13, L"" }, /* New Name */ - { L"SGEP0", L"Set Glyph Element Pointer 0", L"E", L"", 0x13, L"" }, /* Old Name */ - { L"SZP1", L"Set Zone Pointer 1", L"E", L"", 0x14, L"" }, /* New Name */ - { L"SGEP1", L"Set Glyph Element Pointer 1", L"E", L"", 0x14, L"" }, /* Old Name */ - { L"SZP2", L"Set Zone Pointer 2", L"E", L"", 0x15, L"" }, /* New Name */ - { L"SGEP2", L"Set Glyph Element Pointer 2", L"E", L"", 0x15, L"" }, /* Old Name */ - { L"SZPS", L"Set Zone PointerS", L"E", L"", 0x16, L"" }, /* New Name */ - { L"SGEPS", L"Set Glyph Element PointerS", L"E", L"", 0x16, L"" }, /* Old Name */ - { L"SLOOP", L"Set Loop Variable", L"V", L"", asm_SLOOP, L"" }, - { L"RTG", L"Round To Grid", L"", L"", 0x18, L"" }, - { L"RTHG", L"Round To Half Grid", L"", L"", 0x19, L"" }, - { L"SMD", L"Set Minimum Distance", L"D", L"", 0x1A, L"" }, - { L"ELSE", L"Else", L"", L"", 0x1B, L"" }, - { L"JR", L"Jump Relative", L"L", L"", 0x1C, L"" }, - { L"JMPR", L"Jump Relative", L"L", L"", 0x1C, L"" }, /* name documented in TrueType doc */ - { L"SCVTCI", L"Set Control Value Table Cut In", L"D", L"", 0x1D, L"" }, - { L"SSWCI", L"Set Single Width Cut In", L"D", L"", 0x1E, L"" }, - { L"SSW", L"Set Single Width", L"D", L"", 0x1F, L"" }, - { L"DUP", L"Duplicate", L"*", L"**", 0x20, L"" }, - { L"POP", L"POP top element off the stack", L"*", L"", 0x21, L"" }, - { L"CLEAR", L"CLEAR entire stack", L"&", L"", 0x22, L"" }, - { L"SWAP", L"SWAP two top elements", L"**", L"**", 0x23, L"" }, - { L"DEPTH", L"DEPTH of stack", L"", L"V", 0x24, L"" }, - { L"CINDEX", L"Copy INDEXed element to the top of the stack", L"V", L"*", 0x25, L"" }, - { L"MINDEX", L"Move INDEXed element to the top of the stack", L"V", L"", 0x26, L"" }, - { L"ALIGNPTS", L"ALIGN PoinTS", L"PP", L"", 0x27, L"" }, - { L"RAW", L"Read Advance Width", L"", L"D", 0x28, L"" }, - { L"UTP", L"UnTouch Point", L"P", L"", 0x29, L"" }, - { L"LOOPCALL", L"LOOP while CALLing function", L"VF", L"", 0x2A, L"" }, - { L"CALL", L"CALL function", L"F", L"", 0x2B, L"" }, - { L"FDEF", L"Function DEFinition", L"F", L"", 0x2C, L"" }, - { L"ENDF", L"END Function definition", L"", L"", 0x2D, L"" }, - { L"MDAP", L"Move Direct Absolute Point", L"P", L"", 0x2E, L"R" }, - { L"IUP", L"Interpolate Untouched Points", L"", L"", 0x30, L"A" }, - { L"SHP", L"SHift Point", L"p", L"", 0x32, L"1" }, - { L"SHC", L"SHift Contour", L"C", L"", 0x34, L"1" }, - { L"SHZ", L"SHift Zone", L"E", L"", 0x36, L"1"}, /* NEW NAME */ - { L"SHE", L"SHift Element", L"E", L"", 0x36, L"1"}, /* OLD NAME */ - { L"SHPIX", L"SHift by fractional PIXel amount", L"pD", L"", 0x38, L"" }, - { L"IP", L"Interpolate Point", L"p", L"", 0x39, L"" }, - { L"MSIRP", L"Move Stack Indirect Relative Point", L"PD", L"", 0x3A, L"M" }, - { L"ALIGNRP", L"ALIGN Relative Point", L"p", L"", 0x3C, L"" }, - { L"RTDG", L"Round To Double Grid", L"", L"", 0x3D, L"" }, - { L"MIAP", L"Move Indirect Absolute Point", L"PI", L"", 0x3E, L"R" }, - { L"NPUSHB", L"PUSH n Bytes", L"NB", L"", 0x40, L"" }, - { L"NPUSHW", L"PUSH n Words", L"NW", L"", 0x41, L"" }, - { L"WS", L"Write Store", L"J*", L"", 0x42, L"" }, - { L"RS", L"Read Store", L"J", L"D", 0x43, L"" }, - { L"WCVTP", L"Write Control Value Table in Pixel units", L"ID", L"", 0x44, L"" }, - { L"RCVT", L"Read Control Value Table", L"I", L"D", 0x45, L"" }, - { L"GC", L"Get Coordinate value", L"P", L"D", 0x46, L"O" }, - { L"SCFS", L"Set Coordinate value From Stack", L"PD", L"", 0x48, L"" }, - { L"MD", L"Measure Distance", L"PP", L"D", 0x49, L"O"}, - { L"MPPEM", L"Measure Pixels Per EM", L"", L"V", 0x4B, L"" }, - { L"MPS", L"Measure Pointsize", L"", L"V", 0x4C, L"" }, - { L"FLIPON", L"set autoFLIP boolean ON", L"", L"", 0x4D, L"" }, - { L"FLIPOFF", L"set autoFLIP boolean OFF", L"", L"", 0x4E, L"" }, - { L"DEBUG", L"DEBUGger call", L"*", L"", 0x4F, L"" }, - { L"LT", L"Less Than", L"**", L"B", 0x50, L"" }, - { L"LTEQ", L"Less Than or EQual", L"**", L"B", 0x51, L"" }, - { L"GT", L"Greater Than", L"**", L"B", 0x52, L"" }, - { L"GTEQ", L"Greater Than or EQual", L"**", L"B", 0x53, L"" }, - { L"EQ", L"EQual", L"**", L"B", 0x54, L"" }, - { L"NEQ", L"Not EQual", L"**", L"B", 0x55, L"" }, - { L"ODD", L"ODD", L"*", L"B", 0x56, L"" }, - { L"EVEN", L"EVEN", L"*", L"B", 0x57, L"" }, - { L"IF", L"IF", L"B", L"", 0x58, L"" }, - { L"EIF", L"End IF", L"", L"", 0x59, L"" }, - { L"AND", L"AND", L"BB", L"B", 0x5A, L"" }, - { L"OR", L"OR", L"BB", L"B", 0x5B, L"" }, - { L"NOT", L"NOT", L"B", L"B", 0x5C, L"" }, - { L"DELTAP1", L"DELTA Point 1", L"***", L"", 0x5D, L"" }, - { L"SDB", L"Set Delta Base", L"*", L"", 0x5E, L"" }, - { L"SDS", L"Set Delta Shift", L"*", L"", 0x5F, L"" }, - { L"ADD", L"ADD", L"**", L"*", 0x60, L"" }, - { L"SUB", L"SUBTRACT", L"**", L"*", 0x61, L"" }, - { L"DIV", L"DIVide", L"**", L"*", 0x62, L"" }, - { L"MUL", L"MULtiply", L"**", L"*", 0x63, L"" }, - { L"ABS", L"ABSolute value", L"*", L"*", 0x64, L"" }, - { L"NEG", L"NEGate", L"*", L"*", 0x65, L"" }, - { L"FLOOR", L"FLOOR", L"*", L"*", 0x66, L"" }, - { L"CEILING", L"CEILING", L"*", L"*", 0x67, L"" }, - { L"ROUND", L"ROUND", L"*", L"*", 0x68, L"Cc" }, - { L"NROUND", L"No ROUND", L"*", L"*", 0x6C, L"Cc" }, - { L"WCVTF", L"Write Control Value Table in Funits", L"I*", L"", 0x70, L"" }, - { L"DELTAP2", L"DELTA Point 2", L"***", L"", 0x71, L"" }, - { L"DELTAP3", L"DELTA Point 3", L"***", L"", 0x72, L"" }, - { L"DELTAC1", L"DELTA Cvt 1", L"***", L"", 0x73, L"" }, - { L"DELTAC2", L"DELTA Cvt 2", L"***", L"", 0x74, L"" }, - { L"DELTAC3", L"DELTA Cvt 3", L"***", L"", 0x75, L"" }, - { L"SROUND", L"Super Round", L"*", L"", 0x76, L"" }, - { L"S45ROUND", L"Super 45 Round", L"*", L"", 0x77, L"" }, - { L"JROT", L"Jump Relative On True", L"LB", L"", 0x78, L"" }, - { L"JROF", L"Jump Relative On False", L"LB", L"", 0x79, L"" }, - { L"ROFF", L"Rounding Off", L"", L"", 0x7A, L"" }, - { L"RUTG", L"Round Up To Grid", L"", L"", 0x7C, L"" }, - { L"RDTG", L"Round Down To Grid", L"", L"", 0x7D, L"" }, - { L"SANGW", L"Set Angle Weight", L"*", L"", 0x7E, L"" }, - { L"AA", L"Adjust Angle", L"P", L"", 0x7F, L"" }, - { L"FLIPPT", L"Flip Point", L"p", L"", 0x80, L"" }, - { L"FLIPRGON", L"Flip Range On", L"PP", L"", 0x81, L"" }, - { L"FLIPRGOFF", L"Flip Range Off", L"PP", L"", 0x82, L"" }, - { L"USER83", L"User Defined 83", L"", L"", 0x83, L"" }, - { L"USER84", L"User Defined 84", L"", L"", 0x84, L"" }, - { L"SCANCTRL", L"Scan Converter Control", L"*", L"", 0x85, L"" }, - { L"SDPVTL", L"Set Dual Projection Vector To Line", L"PP", L"", 0x86, L"R" }, - { L"GETINFO", L"GET miscellaneous INFO", L"*", L"*", 0x88, L"" }, - { L"IDEF", L"Instruction DEFinition", L"*", L"", 0x89, L"" }, - { L"ROLL", L"ROLL 3 top stack elements", L"***", L"***", 0x8a, L"" }, /* NEW NAME */ - { L"ROT", L"ROTate 3 top stack elements", L"***", L"***", 0x8a, L"" }, /* OLD NAME */ - { L"MAX", L"MAXimum", L"**", L"*", 0x8b, L"" }, - { L"MIN", L"MINimum", L"**", L"*", 0x8c, L"" }, - { L"SCANTYPE", L"Scan Type", L"*", L"", 0x8d, L"" }, - { L"INSTCTRL", L"Instruction Control", L"*", L"", 0x8e, L"" }, - { L"USER8F", L"User Defined 8F", L"", L"", 0x8F, L"" }, - { L"USER90", L"User Defined 90", L"", L"", 0x90, L"" }, - { L"USER91", L"User Defined 91", L"", L"", 0x91, L"" }, - { L"GETVARIATION", L"Get Variation", L"", L"V", 0x91, L"" }, - { L"USER92", L"User Defined 92", L"", L"", 0x92, L"" }, - { L"USER93", L"User Defined 93", L"", L"", 0x93, L"" }, - { L"USER94", L"User Defined 94", L"", L"", 0x94, L"" }, - { L"USER95", L"User Defined 95", L"", L"", 0x95, L"" }, - { L"USER96", L"User Defined 96", L"", L"", 0x96, L"" }, - { L"USER97", L"User Defined 97", L"", L"", 0x97, L"" }, - { L"USER98", L"User Defined 98", L"", L"", 0x98, L"" }, - { L"USER99", L"User Defined 99", L"", L"", 0x99, L"" }, - { L"USER9A", L"User Defined 9A", L"", L"", 0x9A, L"" }, - { L"USER9B", L"User Defined 9B", L"", L"", 0x9B, L"" }, - { L"USER9C", L"User Defined 9C", L"", L"", 0x9C, L"" }, - { L"USER9D", L"User Defined 9D", L"", L"", 0x9D, L"" }, - { L"USER9E", L"User Defined 9E", L"", L"", 0x9E, L"" }, - { L"USER9F", L"User Defined 9F", L"", L"", 0x9F, L"" }, - { L"USERA0", L"User Defined A0", L"", L"", 0xA0, L"" }, - { L"USERA1", L"User Defined A1", L"", L"", 0xA1, L"" }, - { L"USERA2", L"User Defined A2", L"", L"", 0xA2, L"" }, - { L"USERA3", L"User Defined A3", L"", L"", 0xA3, L"" }, - { L"USERA4", L"User Defined A4", L"", L"", 0xA4, L"" }, - { L"USERA5", L"User Defined A5", L"", L"", 0xA5, L"" }, - { L"USERA6", L"User Defined A6", L"", L"", 0xA6, L"" }, - { L"USERA7", L"User Defined A7", L"", L"", 0xA7, L"" }, - { L"USERA8", L"User Defined A8", L"", L"", 0xA8, L"" }, - { L"USERA9", L"User Defined A9", L"", L"", 0xA9, L"" }, - { L"USERAA", L"User Defined AA", L"", L"", 0xAA, L"" }, - { L"USERAB", L"User Defined AB", L"", L"", 0xAB, L"" }, - { L"USERAC", L"User Defined AC", L"", L"", 0xAC, L"" }, - { L"USERAD", L"User Defined AD", L"", L"", 0xAD, L"" }, - { L"USERAE", L"User Defined AE", L"", L"", 0xAE, L"" }, - { L"USERAF", L"User Defined AF", L"", L"", 0xAF, L"" }, - { L"PUSHB", L"PUSH Bytes", L"", L"", 0xB0, L"P" }, /* fix this */ - { L"PUSHW", L"PUSH Words", L"", L"", 0xB8, L"P" }, - { L"MDRP", L"Move Direct Relative Point", L"P", L"", 0xC0, L"M>RCc" }, - { L"MIRP", L"Move Indirect Relative Point", L"PI", L"", 0xE0, L"M>RCc" }, - - { L"DLTP1", L"DELTA Point 1, direct", L"***", L"", 0x15D, L"" }, - { L"DLTP2", L"DELTA Point 2, direct", L"***", L"", 0x171, L"" }, - { L"DLTP3", L"DELTA Point 3, direct", L"***", L"", 0x172, L"" }, - { L"DLTC1", L"DELTA Cvt 1, direct", L"***", L"", 0x173, L"" }, - { L"DLTC2", L"DELTA Cvt 2, direct", L"***", L"", 0x174, L"" }, - { L"DLTC3", L"DELTA Cvt 3, direct", L"***", L"", 0x175, L"" }, - /* Jie 6-22-90 */ - /* #define FakeCode 0xAF*/ - #define FakeCode 0xFFFF - { L"OFFSET", L"Component character", L"V**", L"", FakeCode, L"R" }, - { L"SOFFSET", L"Component character", L"V******", L"", FakeCode, L"R" }, - { L"ANCHOR", L"Component character", L"V**", L"", FakeCode, L"" }, - { L"SANCHOR", L"Component character", L"V******", L"", FakeCode, L"" }, - { L"OVERLAP", L"Component character", L"", L"", FakeCode, L"" }, - { L"NONOVERLAP",L"Component character", L"", L"", FakeCode, L"" }, - { L"USEMYMETRICS",L"Component character", L"", L"", FakeCode, L"" }, - { L"SCALEDCOMPONENTOFFSET",L"Component character", L"", L"", FakeCode, L"" }, - { L"UNSCALEDCOMPONENTOFFSET",L"Component character", L"", L"", FakeCode, L"" }, -}; - -/* if we add/remove instructions, we should update tt_TOTALNUMBEROFINSTRUCTIONS in the .h file too */ -#define TOTALNUMBEROFINSTRUCTIONS (sizeof(tt_instruction) / sizeof(tt_InstructionType)) - -const asm_BooleanTranslationType asm_booleanTranslation1[] = { - { L'A', L'X', 1 }, - { L'A', L'Y', 0 }, - { L'O', L'O', 1 }, - { L'O', L'N', 0 }, - { L'R', L'R', 1 }, - { L'R', L'r', 0 }, - { L'M', L'M', 1 }, - { L'M', L'm', 0 }, - { L'1', L'1', 1 }, - { L'1', L'2', 0 }, - { L'>', L'>', 1 }, - { L'>', L'<', 0 }, - { L'C', L'G', 0 }, - { L'C', L'B', 0 }, - { L'C', L'W', 0 }, - { L'c', L'r', 0 }, - { L'c', L'l', 1 }, - { L'c', L'h', 2 }, - { L'P', L'1', 0 }, - { L'P', L'2', 1 }, - { L'P', L'3', 2 }, - { L'P', L'4', 3 }, - { L'P', L'5', 4 }, - { L'P', L'6', 5 }, - { L'P', L'7', 6 }, - { L'P', L'8', 7 } -}; - -#define NumberOfBooleanTranslations (sizeof(asm_booleanTranslation1) / sizeof(asm_BooleanTranslationType)) - -#define TOTALNUMBEROFSWITCH (sizeof(tt_CompilerSwitch) / sizeof(tt_CompilerSwitchType)) - - - -/**** label related code *****/ -/**** moved and adapted from label.c */ - -#define MAXJRPAIR 200 /* max. number of labels "#L100" or JR[] - * lines in one block (#BEGIN-#END - */ -#define MAXLABELLENGTH 22 - -typedef struct { - wchar_t label[MAXLABELLENGTH]; /* label ["#L100"] */ - short iPos; /* instruction position from #BEGIN */ - short *aPtr; /* start ptr of argument storage */ - - short cArg; - wchar_t *linePtr; /* pointer in the source to be able to display the location of an error */ -}tt_jrWordType; - - -/* - * labels - * e.g - * #L100: - */ -typedef struct { - short num; - tt_jrWordType *lab [MAXJRPAIR]; -}tt_LabelType; - -/* - * When #PUSHON - * JR[], #L100 - * or JROF[], #L100, 1 - */ - -typedef struct { - short num; - tt_jrWordType *jr [MAXJRPAIR]; -}tt_JRtype; - -/* - * One #PUSH statement - * eg. #PUSH, 1,2, B1, W2, 3,4 - */ - -#define PUSH_ARG 256 /* max. num of argument on one PUSH [6] */ - -typedef struct { - wchar_t label[MAXLABELLENGTH]; /* label ["#L100"] */ - short LocalIndex; /* argument index from the beginning of the #PUSH */ - - short IsAByte; - unsigned char *aPtr; /* ptr of argument storage */ - wchar_t *linePtr; /* pointer in the source to be able to display the location of an error */ -}tt_psType; - -typedef struct { - short num; /* number of total PUSH statements in a block */ - tt_psType *ps [MAXJRPAIR]; -}tt_PStype; - -/* - * When #PUSHOFF - * e.g. - * JR[], ( B1 = #L100 ) - */ - -typedef struct { - wchar_t label[MAXLABELLENGTH]; /* Label ["#L100"] */ - wchar_t BWLabel[MAXLABELLENGTH]; /* BW word ["B1"} */ - short iPos; /* instruction position from #BEGIN */ - wchar_t *linePtr; /* pointer in the source to be able to display the location of an error */ -}tt_JrBWwordType; - - -typedef struct { - short num; - tt_JrBWwordType *bw [MAXJRPAIR]; -}tt_JrBWtype; - - -int32_t TT_GetLineLength( wchar_t *p, wchar_t *endP); -int32_t TT_GetLineLength( wchar_t *p, wchar_t *endP) -{ - int32_t LineLength; - - LineLength = 0; - while ( !(*p == L'\x0D') && !(*p == L'\x0A') && (p < endP)) // Allow both '\r' and '\n' to terminate lines. - { - LineLength++; p++; - } - return LineLength; -} - -int32_t TT_GetStringLength( wchar_t *p, wchar_t *endP); -int32_t TT_GetStringLength( wchar_t *p, wchar_t *endP) -{ - int32_t StringLength; - - StringLength = 0; - while ( ( (*p >= L'A' && *p <= L'Z') || (*p >= L'a' && *p <= L'z') || (*p >= L'0' && *p <= L'9') ) && (p < endP)) - { - StringLength++; p++; - } - return StringLength; -} - -void TT_SavePushLabel(wchar_t * CurrentPtr, short numberofLocalArgs,int32_t stringLenth,wchar_t *p,tt_PStype *PS, short * tt_error); -void TT_SavePushLabel(wchar_t * CurrentPtr, short numberofLocalArgs,int32_t stringLenth,wchar_t *p,tt_PStype *PS, short * tt_error) -{ - short i, k; - - for ( k = PS->num-1; k >=0; k--) - { - if (wcsncmp( PS->ps[k]->label, p, stringLenth) == 0 && (int32_t)STRLENW(PS->ps[k]->label) == stringLenth ) - { - *tt_error = tt_DuplicateLabel; - return; - } - } - - - k = PS->num; - PS->ps[k] = ( tt_psType *) NewP (sizeof (tt_psType ) ); - if (PS->ps[k] == NULL) { - *tt_error = tt_NotEnoughMemory; - return; - } - - PS->ps[k]->aPtr = NULL; /* we don't know yet, depend on the optimisation of the PUSH */ - PS->ps[k]->linePtr = CurrentPtr; - - PS->ps[k]->LocalIndex = numberofLocalArgs; /* argument index from the beginning of the #PUSH */ - - PS->ps[k]->IsAByte = false; - if (p[0] == L'B') PS->ps[k]->IsAByte = true; - - for ( i = 0; i < stringLenth; i++) { /* copy label */ - PS->ps[k]->label[i] = p[i]; - } - PS->ps[k]->label[stringLenth] = '\0'; - - PS->num ++; - if ( PS->num >= MAXJRPAIR ) { - *tt_error = tt_TooManyLabels; - } -} - - -wchar_t *TT_ParseNumber( wchar_t *p, wchar_t *endP,short *Number, int32_t * SelectionLength, short * error ); -wchar_t *TT_ParseNumber( wchar_t *p, wchar_t *endP,short *Number, int32_t * SelectionLength, short * error ) -{ - short i, negatif; - int32_t tempNumber,maxNum; - wchar_t *pNumStart; - - /* skip white space */ - while ( *p == L' ' && p < endP) - p++; - - tempNumber = 0; - negatif = false; - i = 0; - - if ( *p == L'-' && p < endP ) - { - negatif = true; - p++; - } - - if ( *p == L'+' && p < endP && !negatif) - /* we accept + or - but don't want -+ together */ - { - p++; - } - - maxNum = negatif ? SHORTMAX : USHORTMAX; // allow full unicode range for glyph index B.St. - - pNumStart = p; - - if ( *p == L'0' && *(p+1) == L'x' && (p+2 < endP)) - { - /* there is an hexadecimal number */ - p++; p++; - while ( ((*p >= L'0' && *p <= L'9') || (*p >= L'A' && *p <= L'F') || (*p >= L'a' && *p <= L'f') ) && p < endP ) - { - - if ( *p >= L'0' && *p <= L'9' ) - { - tempNumber = tempNumber * 16 + (int32_t) *p - (int32_t) L'0'; - } else if ( *p >= L'A' && *p <= L'F') - { - tempNumber = tempNumber * 16 + (int32_t) *p - (int32_t) L'A' + 10; - } else if ( *p >= L'a' && *p <= L'f') - { - tempNumber = tempNumber * 16 + (int32_t) *p - (int32_t) L'a' + 10; - } - if (tempNumber > maxNum) - { - *error = tt_ParseOverflow; - *SelectionLength = (int32_t)((ptrdiff_t)(p - pNumStart) + 1); - return pNumStart; - } - p++; - i++; - } - } else { - while ( *p >= L'0' && *p <= L'9' && p < endP ) - { - tempNumber = tempNumber * 10 + (short) *p - (short) L'0'; - if (tempNumber > maxNum) - { - *error = tt_ParseOverflow; - *SelectionLength = (int32_t)((ptrdiff_t)(p - pNumStart) + 1); - return pNumStart; - } - p++; - i++; - } - } - if (i == 0) *error = tt_UnableToParseArgument; - - *Number = (short)tempNumber; - if (negatif) *Number = - (*Number); - return p; - -} - -/*--------------------- Save to database ----------------*/ - -/* - * Parse *p = "#PUSH, 1,2, B1, W1, 3,4 \x0D" - * - * argStore : start ptr of argument storage - * *argIdex : return total number of arguments [6] - * if label begins with 'B', a byte is reserved - * if label begins with 'W', an integer is reserved - * - * all the information is put into *ps - * if lableFlag = false; memory *ps is freed - */ -wchar_t *TT_ParsePUSHandSave(tt_PStype *ps,wchar_t *CurrentPtr,wchar_t * EOLPtr,short *argStore,short *argIdex, int32_t * SelectionLength, short * tt_error ); -wchar_t *TT_ParsePUSHandSave(tt_PStype *ps,wchar_t *CurrentPtr,wchar_t * EOLPtr,short *argStore,short *argIdex, int32_t * SelectionLength, short * tt_error ) -{ - - - - (*argIdex) = 0; - - while (CurrentPtr <= EOLPtr) - { - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if (CurrentPtr >= EOLPtr) break; - - /* look for the comma */ - if ( *CurrentPtr != L',' ) - { - break; /* it could be a comment */ - } - CurrentPtr = CurrentPtr +1; - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if (CurrentPtr >= EOLPtr) break; - - if ( *CurrentPtr == L'B' || *CurrentPtr == L'W' ) - { - int32_t StringLength; - if ( *CurrentPtr == L'B') argStore[(*argIdex)] = 55; /* reserve the space for the jump, one byte or one word */ - else if (*CurrentPtr == L'W' ) argStore[(*argIdex)] = 5555; - - StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); - if (StringLength < 2) - { - *tt_error = tt_VoidLabel; - return (CurrentPtr); - } - - if (StringLength >= MAXLABELLENGTH) - { - *tt_error = tt_LabelTooLong; - return (CurrentPtr); - } - - TT_SavePushLabel(CurrentPtr, *argIdex,StringLength,CurrentPtr,ps, tt_error); - - CurrentPtr = CurrentPtr + StringLength; - } else { - /* there should be a number */ - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &argStore[(*argIdex)], SelectionLength, tt_error); - if (*tt_error != tt_NoError) return CurrentPtr; - } - (*argIdex) ++; - if (*argIdex == PUSH_ARG) - { - *tt_error = tt_TooManyPushArgs; - return (CurrentPtr-1); - } - } - return CurrentPtr; - - -} - - -/* - * given label = "#L100" - * return the index in the Label->lab[index] - * return -1 on error - */ -static short TT_findLabelPos(tt_LabelType *Label,wchar_t *label, short * tt_error ); -static short TT_findLabelPos(tt_LabelType *Label,wchar_t *label, short * tt_error ) -{ - short i; - - for ( i = 0; i < Label->num; i++) { - if ( ! wcscmp ( label, Label->lab[i]->label) ) { - return Label->lab[i]->iPos; - } - } - *tt_error = tt_LabelNotFound; - return -1; -} - -/* - * repostion for - * JR[], #L100 or JROF[], #L100, 1 - * when #PUSHON - */ -void TT_JRpushON_ReplaceLabel(tt_JRtype *JR,tt_LabelType *Label,short *argStore, short * tt_error); -void TT_JRpushON_ReplaceLabel(tt_JRtype *JR,tt_LabelType *Label,short *argStore, short * tt_error) -{ - short i, JRiPos, labeliPos, delta, index; - - for ( i = 0; i < JR->num; i++) { - labeliPos = TT_findLabelPos( Label, JR->jr[i]->label, tt_error ); - JRiPos = JR->jr[i]->iPos; - - delta = labeliPos - JRiPos; - /* if ( delta < 0 ) delta --; */ - - index = JR->jr[i]->cArg; - /* JR->jr[i]->aPtr[index] = delta; */ - argStore[index] = delta; - /* - printf(L"\n JRlabel=" WIDE_STR_FORMAT L" JRipos = %hd, labeliPos= %hd, delta = %hd", - JR->jr[i]->label, JRiPos, labeliPos, delta ); - */ - } -} - - -/* - * repostion the JR[], ( B1 = #L100) type arguments - */ - -void TT_JRpushOFF_ReplaceLabel(tt_JrBWtype *JrBW,tt_PStype *PS,tt_LabelType *Label, short * tt_error); -void TT_JRpushOFF_ReplaceLabel(tt_JrBWtype *JrBW,tt_PStype *PS,tt_LabelType *Label, short * tt_error) -{ - short i, labeliPos, delta, index; - - for ( i = 0; i < PS->num; i++) { - /* for each #PUSH Bn or Wn, look fot the corresponding JR */ - for ( index = 0; index < JrBW->num; index++) { - if ( ! wcscmp( PS->ps[i]->label,JrBW->bw[index]->BWLabel) ) break; - } - - labeliPos = TT_findLabelPos( Label, JrBW->bw[index]->label, tt_error ); - delta = labeliPos - JrBW->bw[i]->iPos; - - if ( (*PS->ps[i]).IsAByte && (delta > 255 )) - { - *tt_error = tt_JumpTooBigForByte; - } - if ( (*PS->ps[i]).IsAByte && ( delta < 0 )) - { - *tt_error = tt_JumpNegativeForByte; - } - - if ( (*PS->ps[i]).IsAByte ) - { - *(*PS->ps[i]).aPtr = (unsigned char) delta; - } else { - short * sTempPtr; - sTempPtr = (short *) (*PS->ps[i]).aPtr; - *sTempPtr = SWAPW(delta); // B.St. - } - - } -} - - -wchar_t * TT_FindLabelError(tt_PStype *PS, tt_JrBWtype *JrBW, tt_JRtype *JR, tt_LabelType *Label, wchar_t * CurrentPtr, int32_t * SelectionLength, short * tt_error) ; -wchar_t * TT_FindLabelError(tt_PStype *PS, tt_JrBWtype *JrBW, tt_JRtype *JR, tt_LabelType *Label, wchar_t * CurrentPtr, int32_t * SelectionLength, short * tt_error) -{ - short i, k; - - /* look through the push on JR */ - if ( JR->num != 0) - { - for ( k = 0; k < JR->num; k++) { - for ( i = 0; i < Label->num; i++) { - if ( ! wcscmp( JR->jr[k]->label,Label->lab[i]->label) ) break; - } - if ( i >= Label->num ) { - *tt_error = tt_LabelNotFound; - *SelectionLength = (int32_t)STRLENW(JR->jr[k]->label); - return JR->jr[k]->linePtr; - } - } - } - - /* look through the push off JR */ - if ( JrBW->num != 0) - { - for ( k = 0; k < JrBW->num; k++) { - for ( i = 0; i < Label->num; i++) { - if ( ! wcscmp( JrBW->bw[k]->label,Label->lab[i]->label) ) break; - } - if ( i >= Label->num ) { - *tt_error = tt_LabelNotFound; - *SelectionLength = (int32_t)STRLENW(JrBW->bw[k]->label); - return JrBW->bw[k]->linePtr; - } - - /* look through the #PUSH,Bn,Wn for the corresponding label */ - for ( i = 0; i < PS->num; i++) { - if ( ! wcscmp( JrBW->bw[k]->BWLabel,PS->ps[i]->label) ) break; - } - if ( i >= PS->num ) { - *tt_error = tt_LabelNotFound; - *SelectionLength = (int32_t)STRLENW(JrBW->bw[k]->BWLabel); - return JrBW->bw[k]->linePtr; - } - } - } - /* check if every #PUSH,Bn,Wn has a corresponding JR[], */ - if ( PS->num != 0) - { - for ( k = 0; k < PS->num; k++) { - for ( i = 0; i < JrBW->num; i++) { - if ( ! wcscmp( PS->ps[k]->label,JrBW->bw[i]->BWLabel) ) break; - } - if ( i >= JrBW->num ) { - *tt_error = tt_LabelNotFound; - *SelectionLength = (int32_t)STRLENW(PS->ps[k]->label); - return PS->ps[k]->linePtr; - } - } - } - return CurrentPtr; -} - -/* - * Save Label information into struct label_LabelType *Label. - * input: - * char *p = #L100:" - * numberofArgs : number of arguments so far (from #BEGIN). - * numberofInstructions : number of instructions so far (from #BEGIN). - * stringLenth : length of "#L100:" - * - * all the information is put into Label - */ - - -void TT_SaveLabel(short numberofArgs,short numberofInstructions,int32_t stringLenth,wchar_t *p,tt_LabelType *Label, short * tt_error); -void TT_SaveLabel(short numberofArgs,short numberofInstructions,int32_t stringLenth,wchar_t *p,tt_LabelType *Label, short * tt_error) -{ - short i, k; - - for ( k = Label->num-1; k >=0; k--) - { - if (wcsncmp( Label->lab[k]->label, p, stringLenth) == 0 && (int32_t)STRLENW(Label->lab[k]->label) == stringLenth ) - { - *tt_error = tt_DuplicateLabel; - return; - } - } - - - k = Label->num; - Label->lab[k] = ( tt_jrWordType *) NewP (sizeof (tt_jrWordType ) ); - if (Label->lab[k] == NULL) { - *tt_error = tt_NotEnoughMemory; - return; - } - - - Label->lab[k]->linePtr = p; - Label->lab[k]->iPos = numberofInstructions; - - for ( i = 0; i < stringLenth; i++) { /* copy label */ - Label->lab[k]->label[i] = p[i]; - } - Label->lab[k]->label[stringLenth] = '\0'; - - Label->num ++; - if ( Label->num >= MAXJRPAIR ) { - *tt_error = tt_TooManyLabels; - } -} - -//void TT_SavePSLabel(short numberofArgs,short numberofInstructions,short stringLenth,char *p,tt_LabelType *Label, short * tt_error); - -wchar_t * TT_SaveJR(short numberofArgs,short numberofInstructions,wchar_t * CurrentPtr, wchar_t *LabelPtr,int32_t stringLenth, - wchar_t *BWLabelPtr,short BWstringLenth,tt_JRtype *JRList,tt_JrBWtype *JrBW, short *aPtr,int32_t * SelectionLength, short * tt_error); -wchar_t * TT_SaveJR(short numberofArgs,short numberofInstructions,wchar_t * CurrentPtr, wchar_t *LabelPtr,int32_t stringLenth, - wchar_t *BWLabelPtr,short BWstringLenth,tt_JRtype *JRList,tt_JrBWtype *JrBW, short *aPtr,int32_t * SelectionLength, short * tt_error) -{ - short i, k; - - for ( k = JRList->num-1; k >=0; k--) - { - if (wcsncmp( JRList->jr[k]->label, LabelPtr, stringLenth) == 0 && (int32_t)STRLENW(JRList->jr[k]->label) == stringLenth ) - { - *tt_error = tt_DuplicateLabel; - *SelectionLength = stringLenth; - return LabelPtr; - } - } - - if (BWstringLenth != 0) - { - - for ( k = JrBW->num-1; k >=0; k--) - { - if (wcsncmp( JrBW->bw[k]->BWLabel, BWLabelPtr, BWstringLenth) == 0 && (int32_t)STRLENW(JrBW->bw[k]->BWLabel) == stringLenth ) - { - *tt_error = tt_DuplicateLabel; - *SelectionLength = BWstringLenth; - return LabelPtr; - } - } - - k = JrBW->num; - JrBW->bw[k] = ( tt_JrBWwordType *) NewP (sizeof (tt_JrBWwordType ) ); - if (JrBW->bw[k] == NULL) { - *tt_error = tt_NotEnoughMemory; - *SelectionLength = stringLenth; - return LabelPtr; - } - - JrBW->bw[k]->linePtr = LabelPtr; - JrBW->bw[k]->iPos = numberofInstructions; - - for ( i = 0; i < stringLenth; i++) { /* copy label */ - JrBW->bw[k]->label[i] = LabelPtr[i]; - } - JrBW->bw[k]->label[stringLenth] = '\0'; - - for ( i = 0; i < BWstringLenth; i++) { /* copy label */ - JrBW->bw[k]->BWLabel[i] = BWLabelPtr[i]; - } - JrBW->bw[k]->BWLabel[BWstringLenth] = '\0'; - - JrBW->num ++; - if ( JrBW->num >= MAXJRPAIR ) { - *tt_error = tt_TooManyLabels; - *SelectionLength = stringLenth; - return LabelPtr; - } - - } else { - - /* no Bn or Wn label */ - k = JRList->num; - JRList->jr[k] = ( tt_jrWordType *) NewP (sizeof (tt_jrWordType ) ); - if (JRList->jr[k] == NULL) { - *tt_error = tt_NotEnoughMemory; - *SelectionLength = stringLenth; - return LabelPtr; - } - - - JRList->jr[k]->aPtr = aPtr; - JRList->jr[k]->cArg = numberofArgs; - - - JRList->jr[k]->linePtr = LabelPtr; - JRList->jr[k]->iPos = numberofInstructions; - - for ( i = 0; i < stringLenth; i++) { /* copy label */ - JRList->jr[k]->label[i] = LabelPtr[i]; - } - JRList->jr[k]->label[stringLenth] = '\0'; - - JRList->num ++; - if ( JRList->num >= MAXJRPAIR ) { - *tt_error = tt_TooManyLabels; - *SelectionLength = stringLenth; - return LabelPtr; - } - } - return CurrentPtr; -} - - -void TT_FreeAllLabelMemory( tt_PStype *PS, tt_JrBWtype *JrBW, tt_LabelType *Label, tt_JRtype *JR); -void TT_FreeAllLabelMemory( tt_PStype *PS, tt_JrBWtype *JrBW, tt_LabelType *Label, tt_JRtype *JR) -{ - - short k; - - for ( k = Label->num-1; k >=0; k--) DisposeP((void**)&Label->lab[k]); - for ( k = JR->num-1; k >=0; k--) DisposeP((void**)&JR->jr[k] ); - for ( k = JrBW->num-1; k >=0; k--) DisposeP((void**)&JrBW->bw[k]); - for ( k = PS->num-1; k >=0; k--) DisposeP((void**)&PS->ps[k]); - - DisposeP((void**)&Label); - DisposeP((void**)&JR ); - DisposeP((void**)&JrBW); - DisposeP((void**)&PS); -} - -/**** end of label related code ****/ - -short TT_ExpandArgs( const wchar_t *src,wchar_t * dest, short *loop , short * tt_error); -short TT_ExpandArgs( const wchar_t *src,wchar_t * dest, short *loop, short * tt_error ) -// char *src, *dest; -// short *loop; -{ - wchar_t c, *base; - short i; - short usesLoop; - - usesLoop = false; - base = dest; - do { - i = *loop; - c = *src++; - if ( c >= L'a' && c <= L'z' ) { - usesLoop = true; - c -= L'a' - L'A'; /* turn into uppercase */ - while ( i > 1 && c && ( dest-base <= ARGTYPEBUFFER_SIZE )) { - i--; - *dest++ = c; - } - } - if ( dest-base > ARGTYPEBUFFER_SIZE ) { - *tt_error = tt_SLOOPArgumentBufferTooSmall; - return usesLoop; - } - - } while ( (*dest++ = c) != 0); - *loop = i; - - return usesLoop; -} - -/* function to be called before TT_Compile to set the maximum values for the range check */ -void TT_SetRangeCheck(short LastContNb, short LastPointNb, short LastZoneNb, short LastFunctionNb, short LastCvtNumber, short LastStorageIndex); -void TT_SetRangeCheck(short LastContNb, short LastPointNb, short LastElementNb, short LastFunctionNb, short LastCvtNumber, short LastStorageIndex) - -{ - /* Point Number */ - asm_ppDescription1[0].highestValidValue = LastPointNb; - - /* Element Number */ - if ( LastElementNb == 0 ) { - asm_ppDescription1[1].lowestValidValue = 1; - asm_ppDescription1[1].highestValidValue = 1; - } else { - asm_ppDescription1[1].lowestValidValue = 0; - asm_ppDescription1[1].highestValidValue = LastElementNb; - } - - /* function number: */ - asm_ppDescription1[5].highestValidValue = LastFunctionNb; - - - /*Index into control value table:*/ - asm_ppDescription1[6].highestValidValue = LastCvtNumber; - - /*Index into storage area*/ - asm_ppDescription1[7].highestValidValue = LastStorageIndex; - - /*Number of Contours*/ - asm_ppDescription1[10].highestValidValue = LastContNb; -} - - -wchar_t * TT_ReadInstructionBooleans (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned short *InstructionCode, int32_t * Selectionlength, short * tt_error); -wchar_t * TT_ReadInstructionBooleans (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned short *InstructionCode, int32_t * Selectionlength, short * tt_error) -{ -/* the instruction code may be changed according to the boolean flags */ - short booleanCount, booleanShift, NBOfBooleans, found, k; - wchar_t * tempP; - if ( *CurrentPtr != L'[' /* ] for balance */ || CurrentPtr >= EOLPtr) - { - *tt_error = tt_BooleanFlagsMissing; - *Selectionlength = 1; - return CurrentPtr; - }; - tempP = CurrentPtr; - CurrentPtr ++; - NBOfBooleans = (short)STRLENW(tt_instruction[InstructionIndex].booleans) ; - booleanShift = NBOfBooleans; - - for (booleanCount = 0; - // for balance '[' - booleanCount < NBOfBooleans && *CurrentPtr != L']' && CurrentPtr < EOLPtr ; - booleanCount++ ) - { - booleanShift--; - for ( found = k = 0; k < (short)NumberOfBooleanTranslations; k++ ) { - if ( asm_booleanTranslation1[k].type == tt_instruction[InstructionIndex].booleans[booleanCount] && - (asm_booleanTranslation1[k].code) == *CurrentPtr ) - { - found = 1; - *InstructionCode = *InstructionCode + (asm_booleanTranslation1[k].result << booleanShift); - } - } - if (!found) - { - *tt_error = tt_UnrecognizedBoolean; - *Selectionlength = 1; - return CurrentPtr; - } - - CurrentPtr++; - } - - if (booleanCount != NBOfBooleans) - { - *tt_error = tt_WrongNumberOfBoolean; - *Selectionlength = (short)(CurrentPtr-tempP)+1; - return tempP; - }; - - if ( (CurrentPtr >= EOLPtr) || (*CurrentPtr == L',' ) ) - { - *tt_error = tt_MissingClosingBracket; - *Selectionlength = (short)(CurrentPtr-tempP); - return tempP; - }; - - if ( /* [ for balance */ *CurrentPtr != L']' ) - { - *tt_error = tt_TooManyBooleans; - *Selectionlength = (short)(CurrentPtr-tempP)+1; - return tempP; - }; - CurrentPtr ++; - return CurrentPtr; -} - - -short TT_DeltaLevel( unsigned short opCode ); -short TT_DeltaLevel( unsigned short opCode ) -// register unsigned char opCode; -{ - short level; - - level = 0; - if ( opCode == 0x5d || opCode == 0x73 || opCode == 0x15d || opCode == 0x173) { - level = 0; - } else if ( opCode == 0x71 || opCode == 0x74 || opCode == 0x171 || opCode == 0x174) { - level = 1; - } else if ( opCode == 0x72 || opCode == 0x75 || opCode == 0x172 || opCode == 0x175) { - level = 2; - } - return level; -} - -void TT_CompileDelta( tt_deltaPType dArr[], short count, unsigned short insCode, short args[], short *argCount); -void TT_CompileDelta( tt_deltaPType dArr[], short deltaCount, unsigned short insCode, short args[], short *argCount ) -{ - short i, tmp, sCount, valid; - unsigned char argCode; - - valid = 1; - sCount = 0; - - for ( i = 0; i < deltaCount; i++) { - - argCode = (unsigned char) dArr[i].relppem; - - /* claudebe, already checked in the parser - if ( argCode < 0 || argCode > 15 ) { - *tt_error = tt_DeltaOORangePpem; - return; - } */ - - argCode <<= 4; - - /* in direct delta denominator == 1, otherwise we already tested that it is zero */ - - tmp = dArr[i].num; - - tmp += 8; - if ( tmp >= 8 ) tmp--; /* there is no code for a null deplacement */ - - /* DELTASHIFT in the DELTAxx command is constant 3, meaning delta from -8/8 to 8/8 by 1/8 steps - use direct delta DLTxx for other shift or base */ - - if (tmp > 15) tmp = 15; - if (tmp < 0) tmp = 0; - - argCode += tmp; - args[ sCount++ ] = argCode; - args[ sCount++ ] = dArr[i].point; - - } - /* claudebe 11/9/95, raid #73, DELTA without argument in PUSHOFF mode shouldn't produce any argument */ - if (deltaCount != 0) - args[ sCount++ ] = deltaCount; - *argCount = sCount; -} - -void TT_SortAndCombineDeltas(tt_deltaPType dArr[], short *countPtr ); -void TT_SortAndCombineDeltas(tt_deltaPType dArr[], short *countPtr ) -/* original Sampo code, except getting rid of the float and there is no need any more to test for zero div and allow delta of more than one pixel */ -{ - short i, j, change; - short count; - - count = *countPtr; - /* Now sort the deltas */ - for ( change = true; change; ) { - tt_deltaPType tmpDelta; - - change = false; - for ( i = count-2; i >= 0; i-- ) { - if ( (dArr[i].relppem > dArr[i+1].relppem) || ( dArr[i].relppem == dArr[i+1].relppem && dArr[i].point > dArr[i+1].point ) ) { - TT_memSwap( (char *)&dArr[i], (char *)&dArr[i+1], (char *)&tmpDelta, sizeof( tt_deltaPType ) ); - change = true; - } - } - } - - - /* Combine deltas */ - for ( change = true; change; ) { - change = false; - for ( i = count-1; i >= 0; i-- ) { - if (dArr[i].num == 0) { - /* we should almost flag this as an error */ - for ( j = i; j < count-1; j++ ) { - memcpy( (char *)&dArr[j], (char *)&dArr[j+1], sizeof( tt_deltaPType ) ); - } - change = true; - count--; - } else if ( i < (count-1) && dArr[i].relppem == dArr[i+1].relppem && dArr[i].point == dArr[i+1].point && dArr[i].denum == dArr[i+1].denum ) { - dArr[i].num += dArr[i+1].num; - for ( j = i+1; j < count-1; j++ ) { - memcpy( (char *)&dArr[j], (char *)&dArr[j+1], sizeof( tt_deltaPType ) ); - } - change = true; - count--; - } - } - } - *countPtr = count; -} - -short TT_IsDelta( unsigned short opCode ); -short TT_IsDelta( unsigned short opCode ) -// register unsigned char opCode; -{ - return ( opCode == 0x5d || ( opCode >= 0x71 && opCode <= 0x75) || opCode == 0x15d || (opCode >= 0x171 && opCode <= 0x175) ); -} - -short TT_IsPushBW( unsigned short opCode ); -short TT_IsPushBW( unsigned short opCode ) -// register unsigned char opCode; -{ - return ( ( opCode >= 0xB0 && opCode <= 0xBF) || opCode == 0x40 || opCode == 0x41 ); -} - -short TT_IsPushB( unsigned short opCode ); -short TT_IsPushB( unsigned short opCode ) -// register unsigned char opCode; -{ - return ( ( opCode >= 0xB0 && opCode <= 0xB7) || opCode == 0x40 ); -} - -short TT_IsNPushBW( unsigned short opCode ); -short TT_IsNPushBW( unsigned short opCode ) -// register unsigned char opCode; -{ - return ( opCode == 0x40 || opCode == 0x41 ); -} - -short TT_IsDirectDelta( unsigned short opCode ); -short TT_IsDirectDelta( unsigned short opCode ) -// register unsigned char opCode; -{ - return ( opCode == 0x15d || ( opCode >= 0x171 && opCode <= 0x175 ) ); -} - -short TT_IsDeltaC( unsigned short opCode ); -short TT_IsDeltaC( unsigned short opCode ) -// register unsigned char opCode; -{ - return ( (opCode >= 0x73 && opCode <= 0x75) || (opCode >= 0x173 && opCode <= 0x175) ); -} - -short TT_IsJR( unsigned short opCode ); -short TT_IsJR( unsigned short opCode ) -// register unsigned short opCode; -{ - return ( opCode == 0x1C || opCode == 0x78 || opCode == 0x79 ); -} - -short TT_IsBlockInstruction( unsigned short opCode ); -short TT_IsBlockInstruction( unsigned short opCode ) -// register unsigned char opCode; -{ - /* JR or IF, ELSE, FDEF , IDEF */ - return ( opCode == 0x1C || opCode == 0x78 || opCode == 0x79 || opCode == 0x2C || opCode == 0x58 || opCode == 0x89 || opCode == 0x1B ); -} - -short TT_IsIDEF_FDEFInstruction( unsigned short opCode ); -short TT_IsIDEF_FDEFInstruction( unsigned short opCode ) -// register unsigned char opCode; -{ - /* FDEF or IDEF */ - return ( opCode == 0x2C || opCode == 0x89 ); -} - -wchar_t * TT_DecodeDeltaP (wchar_t * CurrentPtr, wchar_t * EOLPtr, wchar_t * EndPtr, short InstructionIndex, unsigned short InstructionCode, short *deltaCount, tt_deltaPType dArr[], int32_t * Selectionlength, short * tt_error); -wchar_t * TT_DecodeDeltaP (wchar_t * CurrentPtr, wchar_t * EOLPtr, wchar_t * EndPtr, short InstructionIndex, unsigned short InstructionCode, short *deltaCount, tt_deltaPType dArr[], int32_t * SelectionLength, short * tt_error) -{ - short tempNum; - wchar_t * tempP; - unsigned char argCode; - *deltaCount = 0; - - /* opening bracket */ - if ( *CurrentPtr != L'[' /* ] for balance */ || CurrentPtr >= EOLPtr) - { - *tt_error = tt_DeltaListMissing; - *SelectionLength = 1; - return CurrentPtr; - }; - CurrentPtr++; - - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - while (CurrentPtr == EOLPtr && CurrentPtr < EndPtr) { - CurrentPtr++; - /* the DELTA command is an exception, the only command that can be spread over several lines, - the previous compiler was allowing this and users were using this, so I need to be backwards compatible! */ - EOLPtr = CurrentPtr + TT_GetLineLength(CurrentPtr, EndPtr); - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - } - - while ( /* [ for balance */ *CurrentPtr != L']' && CurrentPtr < EOLPtr) - { - if (*deltaCount > MAXDELTA) - { - *tt_error = tt_TooManyDeltas; - return CurrentPtr; - }; - - /* round bracket */ - if ( *CurrentPtr != L'(' /* ) for balance */ || CurrentPtr >= EOLPtr) - { - *tt_error = tt_DeltaOpeningParenthesisMissing; - *SelectionLength = 1; - return CurrentPtr; - }; - CurrentPtr++; - - tempP = CurrentPtr; - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); - - if (*tt_error != tt_NoError) return CurrentPtr; - - if (TT_IsDeltaC (InstructionCode)) - { - if (tempNum < asm_ppDescription1[6].lowestValidValue || tempNum > asm_ppDescription1[6].highestValidValue) - { - *tt_error = tt_CVTIndexOutOfRange; - *SelectionLength = (int32_t) (CurrentPtr - tempP); - return tempP; - } - } else { - if (tempNum < asm_ppDescription1[0].lowestValidValue || tempNum > asm_ppDescription1[0].highestValidValue) - { - *tt_error = tt_PointNbOutOfRange; - *SelectionLength = (int32_t) (CurrentPtr - tempP); - return tempP; - } - } - - dArr[*deltaCount].point = tempNum; - - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if ( *CurrentPtr != L'@' || CurrentPtr >= EOLPtr) - { - *tt_error = tt_DeltaAtSignMissing; - *SelectionLength = 1; - return CurrentPtr; - }; - CurrentPtr++; - - tempP = CurrentPtr; - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); - - if (*tt_error != tt_NoError) return CurrentPtr; - - if (TT_IsDirectDelta(InstructionCode)) - { - argCode = (unsigned char) tempNum; - } else { - argCode = (unsigned char)(tempNum - DELTABASE - 16 * TT_DeltaLevel( InstructionCode )); - } - - if ( argCode < 0 || argCode > 15 ) { - *tt_error = tt_PointSizeOutOfRange; - *SelectionLength = (int32_t) (CurrentPtr - tempP); - return tempP; - } - - - dArr[*deltaCount].relppem = argCode; - - tempP = CurrentPtr; - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); - - if (*tt_error != tt_NoError) return CurrentPtr; - - dArr[*deltaCount].num = tempNum; - - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - - if (TT_IsDirectDelta(InstructionCode)) - { - dArr[*deltaCount].denum = 1; - } else { - if ( *CurrentPtr != L'/' || CurrentPtr >= EOLPtr) - { - *tt_error = tt_DeltaDenominatorMissing; - *SelectionLength = 1; - return CurrentPtr; - }; - CurrentPtr++; - - tempP = CurrentPtr; - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); - - if (*tt_error != tt_NoError) return CurrentPtr; - - if (tempNum != 8) /* ######## claudebe nobody should use a different denominator (DELTA command with fixed shift) */ - { - *tt_error = tt_DeltaWrongDenominator; - *SelectionLength = (int32_t) (CurrentPtr - tempP); - return tempP; - }; - - dArr[*deltaCount].denum = tempNum; - } - - - *deltaCount = *deltaCount + 1; - - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - /* round bracket */ - if /* ( for balance */ ( *CurrentPtr != L')' || CurrentPtr >= EOLPtr) - { - *tt_error = tt_DeltaClosingParenthesisMissing; - *SelectionLength = 1; - return CurrentPtr; - }; - CurrentPtr++; - - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - while (CurrentPtr == EOLPtr && CurrentPtr < EndPtr) { - CurrentPtr++; - /* the DELTA command is an exception, the only command that can be spread over several lines, - the previous compiler was allowing this and users were using this, so I need to be backwards compatible! */ - EOLPtr = CurrentPtr + TT_GetLineLength(CurrentPtr, EndPtr); - /* skip spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - } - } - - /* closing bracket */ - if ( /* [ for balance */ *CurrentPtr != L']' || CurrentPtr >= EOLPtr) - { - *tt_error = tt_DeltaClosingBracketMissing; - *SelectionLength = 1; - return CurrentPtr; - }; - CurrentPtr ++; - - /* Now sort the deltas */ - TT_SortAndCombineDeltas( dArr, deltaCount ); - return CurrentPtr; - -} - -wchar_t * TT_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned char InstructionCode, - asm_PushAndPopDescriptionType asm_ppDescription[], short pushOn, wchar_t * ArgTypeBuffer, short *argc,short *args,short *argc2,wchar_t *args2, - wchar_t ** LabelHandle, short * LabelLength, wchar_t ** BWLabelHandle, short * BWLabelLength, int32_t * SelectionLength, short * MaxFunctionDefs, - /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ - short * BinaryOffset, - /* for the DovMan partial compilation feature that flash points referenced by the current command */ - tt_flashingPoints * flashingPoints, - short * tt_error); -wchar_t * TT_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned char InstructionCode, - asm_PushAndPopDescriptionType asm_ppDescription[], short pushOn, wchar_t * ArgTypeBuffer, short *argc,short *args,short *argc2,wchar_t *args2, - wchar_t ** LabelHandle, short * LabelLength, wchar_t ** BWLabelHandle, short * BWLabelLength, int32_t * SelectionLength, short * MaxFunctionDefs, - /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ - short * BinaryOffset, - /* for the DovMan partial compilation feature that flash points referenced by the current command */ - tt_flashingPoints * flashingPoints, - short * tt_error) -{ - short argindex, argNb; - wchar_t * tempP = nullptr; - *argc = 0; - *argc2 = 0; - argindex = 0; - *LabelLength = 0; - *BWLabelLength = 0; - - if (*BinaryOffset == -1) - { - flashingPoints->NumberOfFlashingPoint = 0; - } - - if (pushOn || TT_IsPushBW(InstructionCode)) - { - argNb = (short)STRLENW(ArgTypeBuffer); - - if ( InstructionCode == 0x2A || InstructionCode == 0x2B ) - { /* function calls, we don't know what should be the number of arguments before processing */ - argNb = 255; - }; - - if (TT_IsPushBW(InstructionCode)) - { - if (TT_IsNPushBW(InstructionCode)) - { - argNb = 1; - ArgTypeBuffer[0] = L'A'; - ArgTypeBuffer[1] = 0; - - } else { - short k; - if ( InstructionCode >= 0xB0 && InstructionCode <= 0xB7) - { - /* PUSHB[] */ - argNb = InstructionCode - 0xB0 + 1; - for (k = 0; k < argNb; k++) - { - ArgTypeBuffer[k] = L'A'; - } - ArgTypeBuffer[k] = 0; - - } else { - /* PUSHW[] */ - argNb = InstructionCode - 0xB8 + 1; - for (k = 0; k < argNb; k++) - { - ArgTypeBuffer[k] = L'H'; - } - ArgTypeBuffer[k] = 0; - } - } - } - for (argindex=0;argindex= EOLPtr) break; - - if ( *CurrentPtr != L',' ) - { - break; /* it could be a comment */ - } - CurrentPtr = CurrentPtr + 1; - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if (CurrentPtr >= EOLPtr) break; - - if ( *CurrentPtr == L'*' ) - { - if (TT_IsPushBW(InstructionCode)) - { - *tt_error = tt_WildCardInPush; - return (CurrentPtr); - } - /* there is a wildcard */ - args2[argindex] = L'*'; - *argc2 = *argc2 + 1; - CurrentPtr++; - } else if ( (argNb != 255) && ( ArgTypeBuffer[argindex] == L'L' ) && (*CurrentPtr == L'#')) - { - int32_t StringLength; - CurrentPtr++; - /* we are looking for a label in a JR instruction */ - StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); - if ( StringLength == 0 ) - { - *tt_error = tt_VoidLabel; - return (CurrentPtr); - } - - if (StringLength >= MAXLABELLENGTH) - { - *tt_error = tt_LabelTooLong; - return (CurrentPtr); - } - - *LabelLength = (short)StringLength; - *LabelHandle = CurrentPtr; - - args2[argindex] = L'L'; /* L for Label */ - *argc2 = *argc2 + 1; - CurrentPtr = CurrentPtr + StringLength; - } else if ( (argNb != 255) && ( ArgTypeBuffer[argindex] == L'F' ) && (InstructionCode == 0x2C )) - { - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &args[argindex], SelectionLength, tt_error); - if (*tt_error != tt_NoError) return CurrentPtr; - /* function definition, we need to update MaxFunctionDefs */ - if (args[argindex] > *MaxFunctionDefs) *MaxFunctionDefs = args[argindex] ; - *argc = *argc + 1; - } else { - short foundPP, PPindex; - - /* there should be a number */ - tempP = CurrentPtr; - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &args[argindex], SelectionLength, tt_error); - if (*tt_error != tt_NoError) return CurrentPtr; - - if (argNb != 255) - { - if ( ArgTypeBuffer[argindex] == L'P' && *BinaryOffset == -1 && flashingPoints->NumberOfFlashingPoint < tt_MAXFLASHINGPOINTS) - { - flashingPoints->flashingPoints[flashingPoints->NumberOfFlashingPoint++] = args[argindex]; - } - foundPP = 0; /* Have not found it yet... */ - for (PPindex=0;(PPindex asm_ppDescription[PPindex].highestValidValue)) { - *SelectionLength = (int32_t) (CurrentPtr - tempP); - CurrentPtr = tempP; - if ( ArgTypeBuffer[argindex] == L'P' ) - { - *tt_error = tt_PointNbOutOfRange; - } else if ( ArgTypeBuffer[argindex] == L'I' ) - { - *tt_error = tt_CVTIndexOutOfRange; - } else if ( ArgTypeBuffer[argindex] == L'J' ) - { - *tt_error = tt_StorageIndexOutOfRange; - } else if ( ArgTypeBuffer[argindex] == L'C' ) - { - *tt_error = tt_ContourNbOutOfRange; - } else { - *tt_error = tt_ArgumentOutOfRange; - }/*if */ - } /* if */ - } /*for */ - } /*for*/ - - if ( ArgTypeBuffer[argindex] == L'A' && argindex == 0) - /* we are in a NPUSH and know now the amount of arguments */ - { - short k; - argNb = argNb + args[argindex]; - if ( InstructionCode == 0x40 ) - { - /* NPUSHB[] */ - for (k = 1; k <= args[argindex]; k++) - { - ArgTypeBuffer[k] = L'A'; - } - ArgTypeBuffer[k] = 0; - - } else { - /* NPUSHW[] */ - for (k = 1; k <= args[argindex]; k++) - { - ArgTypeBuffer[k] = L'H'; - } - ArgTypeBuffer[k] = 0; - } - } - } /*if*/ - - *argc = *argc + 1; - if (*tt_error != tt_NoError) return CurrentPtr; - } - - - } - - - if ( InstructionCode == 0x2A || InstructionCode == 0x2B ) - { /* function calls, we don't know waht should be the number of arguments before processing */ - /* we need to check the function number, - FUTURE : we may want to check the function arguments here */ - short foundPP, PPindex; - - if ((InstructionCode == 0x2B && argindex < 1) || (InstructionCode == 0x2A && argindex < 2) ) - { - *tt_error = tt_MissingParameters; - return CurrentPtr; - } - - if (args2[argindex-1] != L'*') - /* check the function number */ - { - foundPP = 0; /* Have not found it yet... */ - for (PPindex=0;(PPindex asm_ppDescription[PPindex].highestValidValue)) { - *tt_error = tt_FunctionNbOutOfRange; - *SelectionLength = (int32_t) (CurrentPtr - tempP); - CurrentPtr = tempP; - } /* if */ - } /* if */ - } /*for */ - } - - } else { - if (argindex < (short)STRLENW(ArgTypeBuffer)) - { - *tt_error = tt_MissingParameters; - return CurrentPtr; - } - } - - return CurrentPtr; - - } else { - - if (TT_IsJR(InstructionCode)) - { - int32_t StringLength; - /* in this case only jump command has parameters */ - - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if ( *CurrentPtr++ != L',' || CurrentPtr >= EOLPtr ) - { - *tt_error = tt_EmptyParameterList; - return (CurrentPtr-1); - } - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if ( *CurrentPtr++ != L'(' || (CurrentPtr >= EOLPtr) ) - { - *tt_error = tt_JRExpectingABracket; - return (CurrentPtr-1); - } - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - /* parse the Bn or Wn label */ - if ( (*CurrentPtr != L'B' && *CurrentPtr != L'W') || (CurrentPtr >= EOLPtr) ) - { - *tt_error = tt_JRExpectingABWLabel; - return (CurrentPtr); - } - - StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); - if (StringLength < 2) - { - *tt_error = tt_VoidLabel; - return (CurrentPtr); - } - - if (StringLength >= MAXLABELLENGTH) - { - *tt_error = tt_LabelTooLong; - return (CurrentPtr); - } - - - *BWLabelLength = (short)StringLength; - *BWLabelHandle = CurrentPtr; - - CurrentPtr = CurrentPtr + StringLength; - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if ( *CurrentPtr++ != L'=' || (CurrentPtr >= EOLPtr) ) - { - *tt_error = tt_JRExpectingAEqual; - return (CurrentPtr-1); - } - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if ( *CurrentPtr++ != L'#' || (CurrentPtr >= EOLPtr) ) - { - *tt_error = tt_JRExpectingALabel; - return (CurrentPtr-1); - } - - - StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); - if (StringLength >= MAXLABELLENGTH) - { - *tt_error = tt_LabelTooLong; - *SelectionLength = StringLength; - return (CurrentPtr); - } - - if ( StringLength == 0 ) - { - *tt_error = tt_VoidLabel; - *SelectionLength = StringLength; - return (CurrentPtr); - } - - *LabelLength = (short)StringLength; - *LabelHandle = CurrentPtr; - - CurrentPtr = CurrentPtr + StringLength; - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if ( *CurrentPtr != L')' || (CurrentPtr >= EOLPtr) ) - { - *tt_error = tt_JRExpectingABracket; - return (CurrentPtr-1); - } - CurrentPtr++; - - } - return CurrentPtr; - } -} - -void TT_StoreArgumentsAndInstruction (unsigned char InstructionCode, short ** aHandle, unsigned char ** iHandle, - short argc, short *args, short argc2,wchar_t *args2, short * tt_error); - -void TT_StoreArgumentsAndInstruction (unsigned char InstructionCode, short ** aHandle, unsigned char ** iHandle, - short argc, short *args, short argc2,wchar_t *args2, short * tt_error) -{ - short TotalArg, j; - - TotalArg = argc + argc2; - - **iHandle = InstructionCode; - *iHandle = *iHandle +1; - - if (TT_IsPushBW(InstructionCode)) - { - /* PUSH arguments goes to the instruction stream */ - short index = 0; - if (TT_IsNPushBW(InstructionCode)) - { - /* number of bytes to push */ - **iHandle = (unsigned char)args[index++]; - *iHandle = *iHandle +1; - - } - if (TT_IsPushB(InstructionCode)) - { - /* arguments are bytes */ - for ( j = index; j < TotalArg; j++ ) { - **iHandle = (unsigned char) args[index++]; - *iHandle = *iHandle +1; - } - } else { - /* arguments are words */ - for ( j = index; j < TotalArg; j++ ) { - short word = args[index++]; - *(short *) (*iHandle) = SWAPW(word); - *iHandle = *iHandle +2; - } - } - - } else { - for ( j = 0; j < TotalArg; j++ ) { - short index; - index = TotalArg - 1 - j; - if ( index < 0 || index > 255 ) { - * tt_error = tt_ArgumentIndexOutOfRange; - } - - if ( args2[index] != L'*' ) - { - // REVIEW: mjan - Do we need to think about byte order here? - // B.St. It doesn't appear to matter, since we're incrementing aHandle by 1 byte at a time - **aHandle = args[index]; /* Reverse since we are going to reverse everything later */ - *aHandle = *aHandle +1; - } - } - } - - -} - - -/* - * - */ -short TT_BytePush( short argStore[], short StartIndex, short numberofArgs, unsigned char *uCharP , tt_PStype * PS); -short TT_BytePush(short argStore[], short StartIndex, short numberofArgs,unsigned char * uCharP ,tt_PStype * PS ) -// short argStore[], numberofArgs; -// register unsigned char *uCharP; -{ - short i, k; - short count; - - count = 0; - if ( numberofArgs <= 8 ) { - uCharP[count++] = 0xB0 + numberofArgs -1;/* PUSHB */ - } else { - uCharP[count++] = 0x40; /* NPUSHB */ - uCharP[count++] = (unsigned char) numberofArgs; - } - if (PS != NULL) - /* fill the iPos value for the Bn PUSH labels of the current PUSH */ - { - for ( k = PS->num-1; k >=0; k--) - { - if ((PS->ps[k]->aPtr == NULL) && (PS->ps[k]->LocalIndex >= StartIndex) && (PS->ps[k]->LocalIndex < StartIndex+numberofArgs) ) - { - PS->ps[k]->aPtr = (unsigned char *) (uCharP + count + PS->ps[k]->LocalIndex - StartIndex); - } - } - } - for ( i = 0; i < numberofArgs; i++ ) { - uCharP[count++] = (unsigned char) argStore[StartIndex+i]; - } - return count; -} - -/* - * - */ -short TT_WordPush( short argStore[], short StartIndex, short numberofArgs, unsigned char *uCharP, tt_PStype * PS ); -short TT_WordPush( short argStore[], short StartIndex ,short numberofArgs,unsigned char * uCharP, tt_PStype * PS ) -// short argStore[], numberofArgs; -// register unsigned char *uCharP; -{ - short i,k; - short count; - short *shortP; - - count = 0; - if ( numberofArgs <= 8 ) { - uCharP[count++] = 0xB8 + numberofArgs -1;/* PUSHW */ - } else { - uCharP[count++] = 0x41; /* NPUSHW */ - uCharP[count++] = (unsigned char) numberofArgs; - } - - if (PS != NULL) - /* fill the iPos value for the Wn PUSH labels of the current PUSH */ - { - for ( k = PS->num-1; k >=0; k--) - { - if ((PS->ps[k]->aPtr == 0) && (PS->ps[k]->LocalIndex >= StartIndex) && (PS->ps[k]->LocalIndex < StartIndex+numberofArgs) ) - { - PS->ps[k]->aPtr = (unsigned char *)(uCharP + count + 2 * (PS->ps[k]->LocalIndex - StartIndex)); - } - } - } - - for ( i = 0; i < numberofArgs; i++ ) { - shortP = (short *)&uCharP[count]; - *shortP = SWAPW(argStore[StartIndex+i]); - count += 2; - } - return count; -} - -short TT_ByteRunLength( short *args, short n ); -/* - * - */ -short TT_ByteRunLength(short * args,short n ) -//short *args, n; -{ - short len; - - for ( len = 0; len < n && *args >= 0 && *args <= 255; args++ ) { - len++; - } - return len; -} - -short TT_OptimizingPushArguments( unsigned char * BinaryOut,unsigned char * BinaryOutMaxPtr, short argStore[], short numberofArgs, tt_PStype * PS, short *tt_error); -short TT_OptimizingPushArguments( unsigned char * BinaryOut,unsigned char * BinaryOutMaxPtr, short argStore[], short numberofArgs, tt_PStype * PS, short *tt_error) -{ - short i, count, argument; - short argCount, n, byteRun, runLength; - short doBytePush, limit; - - - count = 0; - - for ( n = 0; numberofArgs > 0; ) { - argCount = numberofArgs > 255 ? 255 : numberofArgs; - doBytePush = true; - - for ( runLength = 0, i = n; i < (n + argCount); i++ ) { - argument = argStore[i]; - byteRun = TT_ByteRunLength( &argStore[i], (short) (n + argCount - i) ); - - /* if we have a run of bytes of length 3 (2 if first or last) or more it is more - optimal in terms of space to push them as bytes */ - limit = 3; - if ( (runLength == 0) || ((byteRun + i) >= (n + argCount)) ) { - limit = 2; - } - if ( byteRun >= limit ) { - if ( runLength > 0 ) { - argCount = runLength; - doBytePush = false; - break; /*****/ - } else { - argCount = byteRun; - doBytePush = true; - break; /*****/ - } - } - - if ( argument > 255 || argument < 0 ) doBytePush = false; - runLength++; - } - numberofArgs -= argCount; - - - - if ( doBytePush ) { - if (BinaryOut + count + 2 + argCount > BinaryOutMaxPtr) - { - *tt_error = tt_ProgramTooBig; - return 0; - } - count += TT_BytePush( argStore, n, argCount, &BinaryOut[count], PS ); - } else { - if (BinaryOut + count + 2 + 2 *argCount > BinaryOutMaxPtr) - { - *tt_error = tt_ProgramTooBig; - return 0; - } - count += TT_WordPush( argStore, n, argCount, &BinaryOut[count], PS ); - } - n += argCount; - } - return count; -} - - -short TT_WriteOutBlock( unsigned char *BinaryOut,unsigned char *BinaryOutEndPtr, short * BinaryOffset, short AddOffset, short argStore[], - unsigned char insStore[], short numberofArgs, short numberofInstructions, short * tt_error); -short TT_WriteOutBlock( unsigned char *BinaryOut,unsigned char *BinaryOutEndPtr, short * BinaryOffset, short AddOffset, short argStore[], - unsigned char insStore[], short numberofArgs, short numberofInstructions, short * tt_error) -{ - short i, j, k; - short count; - - /* Reverse arguments since we are pushing them */ - k = numberofArgs >> 1; - for ( i = 0; i < k; i++ ) { - j = argStore[i]; - argStore[i] = argStore[numberofArgs-1-i]; - argStore[numberofArgs-1-i] = j; - } - - count = TT_OptimizingPushArguments(BinaryOut, BinaryOutEndPtr, argStore, numberofArgs, (tt_PStype *) NULL, tt_error); - - if (AddOffset && *BinaryOffset != -1) - { - *BinaryOffset = *BinaryOffset + count; - } - - for ( i = 0; i < numberofInstructions; i++ ) { - BinaryOut[ count++ ] = insStore[i]; - } - - return count; -} - -void TT_ResetLastCommandCompilationStatus(tt_CompilationStatus *CompilationStatus); -void TT_ResetLastCommandCompilationStatus(tt_CompilationStatus *CompilationStatus) -{ - CompilationStatus->LastCommandWasAnIF = false; - CompilationStatus->LastCommandWasAnELSE = false; - CompilationStatus->LastCommandWasAnFDEF = false; - CompilationStatus->LastCommandWasAnIDEF = false; - CompilationStatus->LastCommandWasAnJUMP = false; - CompilationStatus->LastCommandWasAnEND = false; -} - -void TT_CheckAndUpdateCompilationStatus(short InstructionCode,tt_CompilationStatus *CompilationStatus,short * tt_error); -void TT_CheckAndUpdateCompilationStatus(short InstructionCode,tt_CompilationStatus *CompilationStatus,short * tt_error) -{ - - if (CompilationStatus->WeAreInPushOnMode) - { - if (CompilationStatus->LastCommandWasAnIF || CompilationStatus->LastCommandWasAnELSE || CompilationStatus->LastCommandWasAnFDEF || - CompilationStatus->LastCommandWasAnIDEF || CompilationStatus->LastCommandWasAnJUMP) - { - *tt_error = tt_ExpectingaBEGIN; - return; - } - } - TT_ResetLastCommandCompilationStatus(CompilationStatus); - - switch ( InstructionCode ) { - case 0x58: /* IF */ - { - CompilationStatus->WeAreInsideAnIF = true; - CompilationStatus->LastCommandWasAnIF = true; - CompilationStatus->NumberOfEmbeddedIF ++; - if (CompilationStatus->NumberOfEmbeddedIF > MAXIFRECURSION) - { - *tt_error = tt_TooManyEmbeddedIF; - return; - } - CompilationStatus->ELSEStatus[CompilationStatus->NumberOfEmbeddedIF] = false; - break; - } - case 0x59: /* EIF */ - { - if ((!CompilationStatus->WeAreInsideAnIF) || (CompilationStatus->NumberOfEmbeddedIF == 0)) - { - *tt_error = tt_EIFwithoutIF; - return; - } - - CompilationStatus->NumberOfEmbeddedIF --; - if (CompilationStatus->NumberOfEmbeddedIF == 0) CompilationStatus->WeAreInsideAnIF = false; - break; - } - case 0x1B: /* ELSE */ - { - if ((!CompilationStatus->WeAreInsideAnIF) || (CompilationStatus->NumberOfEmbeddedIF == 0)) - { - *tt_error = tt_ELSEwithoutIF; - return; - } - if (CompilationStatus->ELSEStatus[CompilationStatus->NumberOfEmbeddedIF]) - { - *tt_error = tt_ELSEwithinELSE; - return; - } - CompilationStatus->ELSEStatus[CompilationStatus->NumberOfEmbeddedIF] = true; - CompilationStatus->LastCommandWasAnELSE = true; - break; - } - case 0x2C: /* FDEF */ - { - if (CompilationStatus->WeAreInsideAnFDEF) - { - *tt_error = tt_FDEFInsideFDEF; - return; - } - if (CompilationStatus->WeAreInsideAnIDEF) - { - *tt_error = tt_FDEFInsideIDEF; - return; - } - CompilationStatus->WeAreInsideAnFDEF = true; - CompilationStatus->LastCommandWasAnFDEF = true; - break; - } - case 0x89: /* IDEF */ - { - if (CompilationStatus->WeAreInsideAnIDEF) - { - *tt_error = tt_IDEFInsideIDEF; - return; - } - if (CompilationStatus->WeAreInsideAnFDEF) - { - *tt_error = tt_IDEFInsideFDEF; - return; - } - CompilationStatus->WeAreInsideAnFDEF = true; - CompilationStatus->LastCommandWasAnFDEF = true; - break; - } - case 0x2D: /* ENDF */ - { - if (!CompilationStatus->WeAreInsideAnFDEF && !CompilationStatus->WeAreInsideAnIDEF) - { - *tt_error = tt_ENDFwithoutFDEF; - return; - } - - CompilationStatus->WeAreInsideAnFDEF = false; - CompilationStatus->WeAreInsideAnIDEF = false; - break; - } - } - - CompilationStatus->LastCommandWasAnEND = false; - -//typedef struct { -// short WeAreInsideAnIF; -// short WeAreInsideTheELSEofAnIF; -// short WeAreInsideAnFDEF; -// short WeAreInsideAnIDEF; -// short WeAreInPushOnMode; -// short WeAreInPrePushMode; -// short WeAreInsideGHBlock; -// short LastCommandWasAnIF; /* used to check for #BEGIN (beginning of a block) */ -// short LastCommandWasAnELSE; /* used to check for #BEGIN (beginning of a block) */ -// short LastCommandWasAnFDEF; /* used to check for #BEGIN (beginning of a block) */ -// short LastCommandWasAnIDEF; /* used to check for #BEGIN (beginning of a block) */ -// short LastCommandWasAnEND; /* to check if the ENDF, ELSE or EIF is preceeded by #END (end of block) */ -//} tt_CompilationStatus; - -} - -wchar_t *TT_SkipEmptyLines( wchar_t *p, wchar_t *endP,short *pLineCount ); -wchar_t *TT_SkipEmptyLines( wchar_t *p, wchar_t *endP,short *pLineCount ) -{ - wchar_t *myP; - - /* skip spaces and empty lines, keep track of the line number */ - for ( myP = 0; myP != p; ) { - myP = p; - while ( *p == L' ' && p < endP) - p++; - while ( (*p == L'\x0D' || *p == L'\x0A') && p < endP) - { - *pLineCount = *pLineCount+1; p++; - } - } - return p; -} - -wchar_t *TT_SkipCommentsAndEmptyLines( wchar_t *p, wchar_t *endP,short *lineCount, short * error ); -wchar_t *TT_SkipCommentsAndEmptyLines( wchar_t *p, wchar_t *endP,short *pLineCount, short * error ) -{ - wchar_t * tempP; - *error = tt_NoError; - p = TT_SkipEmptyLines( p, endP,pLineCount); - - while ( *p == L'/' && *(p+1) == L'*' && p < endP ) { /* skip C style comments */ - tempP = p; - p++; p++; - while ( !(*p == L'*' && *(p+1) == L'/') && p < endP ) { - - if ( *p == L'/' && *(p+1) == L'*' ) { - *error = tt_EmbeddedComment; - return p; - } - if ( *p == '\x0D' || *p == '\x0A') - { - *pLineCount = *pLineCount+1; - } - p++; - - } - if (p == endP) { - *error = tt_UnterminatedComment; - return tempP; /* return the pointer to the beginning of the unterminated comment */ - } - p++; p++; - p = TT_SkipEmptyLines( p, endP,pLineCount); - } - return p; -} - -wchar_t *TT_InnerCompile( - /* source text, pointer to the begining, the end and the cursor position (to be able to trace until that line) */ - wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, - /* pointer to the output buffer, it's maximal length and return the Binary length */ - unsigned char * BinaryOut, char * BinaryOutEndPtr, int32_t * BinaryLength, - /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ - short * BinaryOffset, - /* length of the text to be selected in case of error */ - int32_t * SelectionLength, - /* line number where the first error occur */ - short * ErrorLineNb, - /* return : approximate stack need, higher function number */ - short * StackNeed, short * MaxFunctionDefs, - /* pass the value 1 to the recursion level, used internally to treat recursively embedded #BEGIN #END blocks */ - short RecursionLevel, - tt_CompilationStatus *CompilationStatus, - /* for the DovMan partial compilation feature that flash points referenced by the current command */ - tt_flashingPoints * flashingPoints, - ASMType asmType, /* asmGLYF, asmPREP, or asmFPGM */ - /* error code, return tt_NoError if no error */ - short * tt_error); - -wchar_t *TT_InnerCompile( - /* source text, pointer to the begining, the end and the cursor position (to be able to trace until that line) */ - wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, - /* pointer to the output buffer, it's maximal length and return the Binary length */ - unsigned char * BinaryOut, char * BinaryOutEndPtr, int32_t * BinaryLength, - /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ - short * BinaryOffset, - /* length of the text to be selected in case of error */ - int32_t * SelectionLength, - /* line number where the first error occur */ - short * ErrorLineNb, - /* return : approximate stack need, higher function number */ - short * StackNeed, short * MaxFunctionDefs, - /* pass the value 1 to the recursion level, used internally to treat recursively embedded #BEGIN #END blocks */ - short RecursionLevel, - tt_CompilationStatus *CompilationStatus, - /* for the DovMan partial compilation feature that flash points referenced by the current command */ - tt_flashingPoints * flashingPoints, - ASMType asmType, /* asmGLYF, asmPREP, or asmFPGM */ - /* error code, return tt_NoError if no error */ - short * tt_error) -{ - short LineNb, LastLineCompiled; - int32_t LineLength, SLoopLineLength; - wchar_t *CurrentPtr, *SLoopPtr; - short numberofArgs, numberofInstructions; - short *argStore, *aPtr; - unsigned char *insStore, *iPtr; - short NeedTwoPass = false; /* used to be MyCode */ - short ghtblock ; - short loop; - short args[256], argc, argc2; - wchar_t args2[256]; - wchar_t argTypeBuffer[ARGTYPEBUFFER_SIZE]; - tt_LabelType * Label; - tt_JRtype * JRList; - tt_PStype *PushLabels; - tt_JrBWtype *JrBW; - short AddOffset = false; - unsigned char * InnerBinaryOutMaxPtr,*InnerArgStoreMaxPtr; - - - CompilationStatus->WeAreInPushOnMode = true; /* reset after a #BEGIN, to be backwards compatible */ - CompilationStatus->WeAreInPrePushMode = true; - CompilationStatus->WeAreInsideGHBlock = false; - CompilationStatus->LastCommandWasAnIF = false; - CompilationStatus->LastCommandWasAnELSE = false; - CompilationStatus->LastCommandWasAnFDEF = false; - CompilationStatus->LastCommandWasAnIDEF = false; - CompilationStatus->LastCommandWasAnJUMP = false; - CompilationStatus->LastCommandWasAnEND = false; - - *tt_error = 0; - *BinaryLength = 0; - *SelectionLength = 0; - *tt_error = tt_NoError; - SLoopPtr = StartPtr; - SLoopLineLength = 0; - - - if (RecursionLevel == 1) - { - *BinaryOffset = -1; - AddOffset = true; - } - if (*BinaryOffset == -1) AddOffset = true; - - PushLabels = (tt_PStype *) NewP( sizeof(tt_PStype) ); - if ( PushLabels == NULL) { - *tt_error = tt_NotEnoughMemory; - *SelectionLength = 0; - return StartPtr; - } - - Label = (tt_LabelType *) NewP( sizeof(tt_LabelType) ); - if ( Label == NULL) { - *tt_error = tt_NotEnoughMemory; - *SelectionLength = 0; - DisposeP((void**)&PushLabels); - return StartPtr; - } - - JRList = (tt_JRtype *) NewP( sizeof(tt_JRtype) ); - if ( JRList == NULL) { - *tt_error = tt_NotEnoughMemory; - *SelectionLength = 0; - DisposeP((void**)&Label); - DisposeP((void**)&PushLabels); - return StartPtr; - } - - JrBW = (tt_JrBWtype *) NewP( sizeof(tt_JrBWtype) ); - if ( JrBW == NULL) { - *tt_error = tt_NotEnoughMemory; - *SelectionLength = 0; - DisposeP((void**)&Label); - DisposeP((void**)&PushLabels); - DisposeP((void**)&JRList); - return StartPtr; - } - - Label->num = 0; - JRList->num = 0; - PushLabels->num = 0; - JrBW->num = 0; - - argStore = (short *)NewP( sizeof(short) * (MAXARGUMENTS) ); - if ( argStore == 0 ) { - *tt_error = tt_NotEnoughMemory; - *SelectionLength = 0; - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return StartPtr; - } - insStore = (unsigned char *)NewP( sizeof(char) * (MAXINSTRUCTIONCOUNT) ); - if ( insStore == 0 ) { - *tt_error = tt_NotEnoughMemory; - DisposeP((void**)&argStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return StartPtr; - } - aPtr = argStore; iPtr = insStore; /* set up pointers */ - InnerBinaryOutMaxPtr = (unsigned char *)(insStore + MAXINSTRUCTIONCOUNT); - InnerArgStoreMaxPtr = (unsigned char *)((unsigned char*)argStore + MAXARGUMENTS); - - CurrentPtr = StartPtr; - LastLineCompiled = 0; - ghtblock = false; - loop = 1; - - for ( LineNb = 1; CurrentPtr <= EndPtr && *tt_error == tt_NoError; ) { - short LineInc = 0; - CurrentPtr = TT_SkipCommentsAndEmptyLines( CurrentPtr,EndPtr, &LineInc, tt_error ); - LineNb += LineInc; - *ErrorLineNb += LineInc; - - if ( *tt_error != tt_NoError) - { - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - - *SelectionLength = 2; - return CurrentPtr; - }; - - if ( CurrentPtr == EndPtr) break; - LineLength = TT_GetLineLength(CurrentPtr, EndPtr); - - if (LineNb == LastLineCompiled) - { - *tt_error = tt_TwoInstructionsInSameLine; - *SelectionLength = LineLength; - break; - } - LastLineCompiled = LineNb; - - if (*CurrentPtr == L'#') - /* label or compiler switch */ - { - int32_t StringLength; - - StringLength = TT_GetStringLength (CurrentPtr +1, EndPtr); - if ((*(CurrentPtr + StringLength + 1) == L':') && (CurrentPtr + StringLength + 1 != EndPtr)) - /* this is a label */ - { - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { - numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); - numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); - - if (StringLength >= MAXLABELLENGTH) - { - *tt_error = tt_LabelTooLong; - *SelectionLength = LineLength; - break; - } - - if ( StringLength == 0 ) - { - *tt_error = tt_VoidLabel; - *SelectionLength = LineLength; - break; - } - - TT_SaveLabel( numberofArgs, numberofInstructions, StringLength, CurrentPtr+1, Label, tt_error); - - if ( *tt_error != tt_NoError) - { - *SelectionLength = LineLength; - break; - } - CurrentPtr = CurrentPtr + StringLength+2; /* the # and the : */ - } - } else { - /* this is a compiler switch */ - short i, found, SwitchCode = 0; - for ( found = i = 0; i < (short)TOTALNUMBEROFSWITCH; i++ ) { - if ( StringLength == (short) STRLENW( tt_CompilerSwitch[i].name ) && - wcsncmp( CurrentPtr+1, tt_CompilerSwitch[i].name, StringLength) == 0 ) { - - found = true; - SwitchCode = tt_CompilerSwitch[i].index; - } - }; - if (!found) { - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { - *tt_error = tt_UnknownSwitch; - *SelectionLength = StringLength + 1; - } - } else - { - switch ( SwitchCode ) { - case tt_Push_Switch: - { - wchar_t * EOLPtr; - short mycount; - - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { - short localArgStore[PUSH_ARG], localNumberofArgs; - EOLPtr = CurrentPtr + LineLength; - - TT_ResetLastCommandCompilationStatus(CompilationStatus); - CurrentPtr = CurrentPtr + StringLength+1; - - CurrentPtr = TT_ParsePUSHandSave( PushLabels, CurrentPtr, EOLPtr, localArgStore, &localNumberofArgs, SelectionLength,tt_error ); - - mycount = TT_OptimizingPushArguments(iPtr, InnerBinaryOutMaxPtr, localArgStore, localNumberofArgs,PushLabels, tt_error ); - - numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ - if (localNumberofArgs > numberofArgs) - /* numberofArgs represent what will already be popped from the stack at this point */ - *StackNeed = *StackNeed + localNumberofArgs - numberofArgs; - iPtr += mycount; - } - - break; - } - case tt_PushOn_Switch: - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { -/* should be a warning, too many errors on old fonts */ -// if (CompilationStatus->WeAreInPushOnMode) -// { -// *tt_error = tt_PUSHONwhenAlreadyOn; -// } - CompilationStatus->WeAreInPushOnMode = true; - CurrentPtr = CurrentPtr + StringLength+1; - } - break; - case tt_PushOff_Switch: - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { -// if (!CompilationStatus->WeAreInPushOnMode) -// { -// *tt_error = tt_PUSHOFFwhenAlreadyOff; -// } - if (CompilationStatus->WeAreInPushOnMode) - { - if (CompilationStatus->LastCommandWasAnIF || CompilationStatus->LastCommandWasAnELSE || CompilationStatus->LastCommandWasAnFDEF || - CompilationStatus->LastCommandWasAnIDEF || CompilationStatus->LastCommandWasAnJUMP) - { - *tt_error = tt_ExpectingaBEGIN; - } - } - CompilationStatus->WeAreInPushOnMode = false; - CurrentPtr = CurrentPtr + StringLength+1; - } - break; - case tt_Begin_Switch: - { - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { - int32_t count; - short subStackNeed, tempInsOffset; - tt_CompilationStatus SavedCompilationStatus; - - if (RecursionLevel == MAXBLOCKEMBEDDING) - { - *tt_error = tt_TooManyEnbeddedBlocks; - *SelectionLength = StringLength + 1; - break; - } - TT_ResetLastCommandCompilationStatus(CompilationStatus); - CurrentPtr = CurrentPtr + StringLength+1; - subStackNeed = 0; - - tempInsOffset = 0; - if (*BinaryOffset == -1) tempInsOffset = (short)(ptrdiff_t)(iPtr - insStore); - - SavedCompilationStatus = *CompilationStatus; - CurrentPtr = TT_InnerCompile(CurrentPtr, EndPtr,SelStartPtr, iPtr, (char *)InnerBinaryOutMaxPtr, &count, BinaryOffset, SelectionLength, ErrorLineNb, - &subStackNeed, MaxFunctionDefs, RecursionLevel+1,CompilationStatus, flashingPoints, asmType, tt_error) ; - - /* restore the status that need to be restored */ - CompilationStatus->WeAreInPushOnMode = SavedCompilationStatus.WeAreInPushOnMode; - - if (*tt_error == tt_NoError) - { - if (CompilationStatus->NumberOfEmbeddedIF != SavedCompilationStatus.NumberOfEmbeddedIF) - { - *tt_error = tt_IFgoingAcrossBlocks; - CurrentPtr = CurrentPtr - 4; /* go back before the #END */ - *SelectionLength = 4; - } - if (CompilationStatus->WeAreInsideAnFDEF != SavedCompilationStatus.WeAreInsideAnFDEF) - { - *tt_error = tt_FDEFgoingAcrossBlocks; - CurrentPtr = CurrentPtr - 4; /* go back before the #END */ - *SelectionLength = 4; - } - if (CompilationStatus->WeAreInsideAnIDEF != SavedCompilationStatus.WeAreInsideAnIDEF) - { - *tt_error = tt_IDEFgoingAcrossBlocks; - CurrentPtr = CurrentPtr - 4; /* go back before the #END */ - *SelectionLength = 4; - } - } - - - if (*BinaryOffset != -1) *BinaryOffset = *BinaryOffset + tempInsOffset; - - numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ - if (subStackNeed > numberofArgs) - /* numberofArgs represent what will already be popped from the stack at this point */ - *StackNeed = *StackNeed + subStackNeed - numberofArgs; - - iPtr += count; - } - break; - } - case tt_End_Switch: - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { - TT_ResetLastCommandCompilationStatus(CompilationStatus); - if (RecursionLevel > 1) - { - CurrentPtr = CurrentPtr + StringLength+1; - /* write the code of the block before comming back */ - numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ - numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); /* number of instructions */ - - - if (NeedTwoPass) - { - CurrentPtr = TT_FindLabelError(PushLabels, JrBW, JRList, Label, CurrentPtr, SelectionLength, tt_error) ; - - if (*tt_error != tt_NoError) - { - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; - }; - TT_JRpushON_ReplaceLabel( JRList, Label, argStore, tt_error); - TT_JRpushOFF_ReplaceLabel( JrBW, PushLabels, Label, tt_error); - } - - if (*tt_error == tt_NoError) - *BinaryLength = TT_WriteOutBlock( BinaryOut, (unsigned char *)BinaryOutEndPtr, BinaryOffset, AddOffset, argStore, insStore, numberofArgs, numberofInstructions, tt_error); - - *StackNeed = *StackNeed + numberofArgs; - - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; - } else { - *tt_error = tt_EndWithoutBegin; - *SelectionLength = StringLength + 1; - } - } - break; - case tt_GHTBlockBegin_Switch: - CompilationStatus->WeAreInsideGHBlock = true; - CurrentPtr = CurrentPtr + LineLength; - break; - case tt_GHTBlockEnd_Switch: - CompilationStatus->WeAreInsideGHBlock = false; - CurrentPtr = CurrentPtr + LineLength; - break; - } - } - } - } else { - if (CompilationStatus->WeAreInsideGHBlock) - { - CurrentPtr = CurrentPtr + LineLength; - } else { - /* regular instruction */ - short InstructionIndex, found; - unsigned short InstructionCode = 0; /* unsigned short because of the fake code used to detect composite commands */ - int32_t StringLength; - - StringLength = TT_GetStringLength (CurrentPtr, EndPtr); - - for ( found = InstructionIndex = 0; InstructionIndex < (short)TOTALNUMBEROFINSTRUCTIONS; InstructionIndex++ ) { - if ( StringLength == (short) STRLENW( tt_instruction[InstructionIndex].name ) && - wcsncmp(CurrentPtr, tt_instruction[InstructionIndex].name, StringLength) == 0 ) { - found = true; - InstructionCode = tt_instruction[InstructionIndex].baseCode; - break; - } - } - if (!found) { - *SelectionLength = StringLength; - *tt_error = tt_UnknownInstruction; - } else { - TT_CheckAndUpdateCompilationStatus(InstructionCode,CompilationStatus,tt_error); - if ( *tt_error != tt_NoError) - { - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - *SelectionLength = StringLength; - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; - }; - - if (TT_IsDelta(InstructionCode)) - { - wchar_t * EOLPtr, *LineBegin; - short deltaCount; - short i; - - tt_deltaPType dArr[MAXDELTA]; - LineBegin = CurrentPtr; - EOLPtr = CurrentPtr + LineLength; - /* this is a delta instruction */ - CurrentPtr = TT_DecodeDeltaP( CurrentPtr+StringLength, EOLPtr, EndPtr, InstructionIndex, InstructionCode, &deltaCount, dArr, SelectionLength,tt_error); - - if ((deltaCount == 0) && CompilationStatus->WeAreInPushOnMode && *tt_error == tt_NoError) - { - *tt_error = tt_DELTAWithoutArguments; - *SelectionLength = LineLength; - return LineBegin; - } - if ((deltaCount != 0) && !CompilationStatus->WeAreInPushOnMode && *tt_error == tt_NoError) - { - *tt_error = tt_DELTAWithArguments; - *SelectionLength = LineLength; - return LineBegin; - } - - for ( i = 255; i >= 0; i-- ) { - args2[i] = L' '; - } - argc2 = 0; - - TT_CompileDelta( dArr, deltaCount, (unsigned char) InstructionCode, args, &argc ); - if (CurrentPtr > SelStartPtr && *BinaryOffset == -1) - { - *BinaryOffset = (short)(ptrdiff_t)(iPtr - insStore); - } - InstructionCode = InstructionCode & 0xFF; /* for the diret delta */ - - if (iPtr + 1 > InnerBinaryOutMaxPtr) - { - *tt_error = tt_ProgramTooBig; - return CurrentPtr; - } - if ((char*)aPtr + argc + argc2 > (char*)InnerArgStoreMaxPtr) - { - *tt_error = tt_TooManyArguments; - return CurrentPtr; - } - TT_StoreArgumentsAndInstruction((unsigned char) InstructionCode, &aPtr, &iPtr, argc, args, argc2, args2, tt_error); - - } else if (InstructionCode == FakeCode) { - /* this is a composite information */ - *tt_error = tt_CompositeCode; - *SelectionLength = LineLength; - } else { - /* this is a regular instruction */ - wchar_t * LabelPtr, *BWLabelPtr; - short LabelLength, BWLabelLength; - wchar_t * EOLPtr, *LineBegin; - LineBegin = CurrentPtr; - EOLPtr = CurrentPtr + LineLength; - CurrentPtr = TT_ReadInstructionBooleans (CurrentPtr+StringLength, EOLPtr, InstructionIndex, &InstructionCode, SelectionLength, tt_error); - - if (TT_IsPushBW(InstructionCode) && CompilationStatus->WeAreInPushOnMode && *tt_error == tt_NoError) - { - *tt_error = tt_PUSHBWInPushON; - *SelectionLength = LineLength; - return LineBegin; - } - if ( *tt_error != tt_NoError) - { - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; - }; - - /* expand the argument list for a command affected by the SLOOP */ - if ( TT_ExpandArgs( tt_instruction[InstructionIndex].pops, argTypeBuffer, &loop, tt_error ) ) { - loop = 1; - } - if ( *tt_error != tt_NoError) - { - /* error should be SLOOP argument too big */ - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - *SelectionLength = SLoopLineLength; - return SLoopPtr; - }; - - CurrentPtr = TT_ReadInstructionParameters (CurrentPtr, EOLPtr, InstructionIndex, (unsigned char) InstructionCode, asm_ppDescription1, CompilationStatus->WeAreInPushOnMode, - argTypeBuffer, &argc, args, &argc2, args2, &LabelPtr, &LabelLength, &BWLabelPtr, &BWLabelLength, - SelectionLength, MaxFunctionDefs,BinaryOffset, flashingPoints,tt_error); - if ( *tt_error != tt_NoError) - { - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; - }; - - if ( InstructionCode == asm_SLOOP ) { - /* OK we got the arguments */ - if ( CompilationStatus->WeAreInPushOnMode ) { - SLoopLineLength = LineLength; - SLoopPtr = LineBegin; - loop = args[0]; - } else { - loop = 1; - } - } - - if (TT_IsJR(InstructionCode)) { - numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ - numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); /* number of instructions */ - - if (LabelLength != 0 || BWLabelLength != 0) - { - NeedTwoPass = true; /* we have a label to replace by the real value in the second pass */ - - CurrentPtr = TT_SaveJR( numberofArgs+argc /* we need to count for the boolean if given as an argument, hence the + argc */ - , numberofInstructions, CurrentPtr, LabelPtr, LabelLength, BWLabelPtr, BWLabelLength, JRList, JrBW, argStore, SelectionLength , tt_error); - } - }; - - - if (InstructionCode == 0x8e /* INSTCTRL */ && asmType != asmPREP) - { - *tt_error = tt_INSTCTRLnotInPreProgram; - *SelectionLength = LineLength; - return LineBegin; - } - - if (TT_IsIDEF_FDEFInstruction(InstructionCode) && asmType == asmGLYF) - { - *tt_error = tt_IDEF_FDEFinGlyphProgram; - *SelectionLength = LineLength; - return LineBegin; - } - - if (CurrentPtr > SelStartPtr && *BinaryOffset == -1) - { - *BinaryOffset = (short)(ptrdiff_t)(iPtr - insStore); - } - - if (TT_IsPushBW(InstructionCode)) - { - if (iPtr + 1 + 2 * argc > InnerBinaryOutMaxPtr) - { - *tt_error = tt_ProgramTooBig; - return CurrentPtr; - } - } else - { - if (iPtr + 1 > InnerBinaryOutMaxPtr) - { - *tt_error = tt_ProgramTooBig; - return CurrentPtr; - } - if ((char*)aPtr + argc + argc2 > (char*)InnerArgStoreMaxPtr) - { - *tt_error = tt_TooManyArguments; - return CurrentPtr; - } - } - - TT_StoreArgumentsAndInstruction((unsigned char)InstructionCode, &aPtr, &iPtr, argc, args, argc2, args2, tt_error); - } - } - - } - } - - - - } - - if (*tt_error != tt_NoError) - { - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; - }; - - if (RecursionLevel > 1) - { - *tt_error = tt_MissingEndBlock; - } else { - /* write the new code */ - numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ - numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); /* number of instructions */ - - if (NeedTwoPass) - { - CurrentPtr = TT_FindLabelError(PushLabels, JrBW, JRList, Label, CurrentPtr, SelectionLength, tt_error) ; - - if (*tt_error != tt_NoError) - { - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; - }; - TT_JRpushON_ReplaceLabel( JRList, Label, argStore, tt_error); - TT_JRpushOFF_ReplaceLabel( JrBW, PushLabels, Label, tt_error); - } - - if (*tt_error == tt_NoError) - *BinaryLength = TT_WriteOutBlock( BinaryOut, (unsigned char *)BinaryOutEndPtr, BinaryOffset, AddOffset, argStore, insStore, numberofArgs, numberofInstructions, tt_error); - *StackNeed = *StackNeed + numberofArgs; - } - - DisposeP((void**)&argStore); - DisposeP((void**)&insStore); - TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); - return CurrentPtr; -} - - -wchar_t *TT_Compile( - /* source text, pointer to the begining, the end and the cursor position (to be able to trace until that line) */ - wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, - /* pointer to the output buffer, it's maximal length and return the length of the resulting binary */ - unsigned char * BinaryOut, short MaxBinaryLength, int32_t * BinaryLength, - /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ - short * BinaryOffset, - /* length of the text to be selected in case of error */ - int32_t * SelectionLength, - /* line number where the first error occur */ - short * ErrorLineNb, - /* return : approximate stack need, higher function number */ - short * StackNeed, short * MaxFunctionDefs, - - /* for the DovMan partial compilation feature that flash points referenced by the current command */ - tt_flashingPoints * flashingPoints, - - ASMType asmType, /* asmGLYF, asmPREP, or asmFPGM */ - /* error code, return tt_NoError if no error */ - short * tt_error); - -wchar_t *TT_Compile(wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, unsigned char * BinaryOut, int32_t MaxBinaryLength, - int32_t * BinaryLength, short * BinaryOffset, int32_t * SelectionLength, short * ErrorLineNb, - short * StackNeed, short * MaxFunctionDefs, - /* for the DovMan partial compilation feature that flash points referenced by the current command */ - tt_flashingPoints * flashingPoints, - ASMType asmType, short * tt_error) -{ - tt_CompilationStatus CompilationStatus; - wchar_t * Result; - char * BinaryOutEndPtr; - *ErrorLineNb = 0; - - BinaryOutEndPtr = (char *) (BinaryOut + MaxBinaryLength); - - CompilationStatus.WeAreInsideAnIF = false; - CompilationStatus.NumberOfEmbeddedIF = 0; - CompilationStatus.WeAreInsideAnFDEF = false; - CompilationStatus.WeAreInsideAnIDEF = false; - CompilationStatus.WeAreInPushOnMode = true; - CompilationStatus.WeAreInPrePushMode = true; - CompilationStatus.WeAreInsideGHBlock = false; - CompilationStatus.LastCommandWasAnIF = false; - CompilationStatus.LastCommandWasAnELSE = false; - CompilationStatus.LastCommandWasAnFDEF = false; - CompilationStatus.LastCommandWasAnIDEF = false; - CompilationStatus.LastCommandWasAnJUMP = false; - CompilationStatus.LastCommandWasAnEND = false; - - flashingPoints->NumberOfFlashingPoint = 0; - - Result = TT_InnerCompile(StartPtr, EndPtr, SelStartPtr, BinaryOut, BinaryOutEndPtr, - BinaryLength, BinaryOffset, SelectionLength, ErrorLineNb, - StackNeed, MaxFunctionDefs, 1, &CompilationStatus, flashingPoints, asmType, tt_error); - - if (*tt_error != tt_NoError) return Result; /* avoid to rewrite the error code by a junk code */ - - if (CompilationStatus.NumberOfEmbeddedIF != 0) - { - *tt_error = tt_IFwithoutEIF; - } - if (CompilationStatus.WeAreInsideAnFDEF) - { - *tt_error = tt_FDEFwithoutENDF; - } - if (CompilationStatus.WeAreInsideAnIDEF) - { - *tt_error = tt_IDEFwithoutENDF; - } - - return Result; -} - -void TT_GetErrorString (short ErrorNb, wchar_t * ErrorString); -void TT_GetErrorString (short ErrorNb, wchar_t * ErrorString) -{ - - switch (ErrorNb ) { - case tt_NoError: - swprintf( ErrorString, L"There is no Error"); - break; - case tt_EmbeddedComment: - swprintf( ErrorString, L"Nested comment"); - break; - case tt_UnterminatedComment: - swprintf( ErrorString, L"Unterminated comment"); - break; - case tt_UnknownSwitch: - swprintf( ErrorString, L"Unknown compiler switch"); - break; - case tt_UnknownInstruction: - swprintf( ErrorString, L"Unknown instruction"); - break; - case tt_TwoInstructionsInSameLine: - swprintf( ErrorString, L"End of line expected"); - break; - case tt_BooleanFlagsMissing: - swprintf( ErrorString, L"bool flags missing"); - break; - case tt_WrongNumberOfBoolean: - swprintf( ErrorString, L"Wrong number of boolean flags"); - break; - case tt_TooManyBooleans: - swprintf( ErrorString, L"Too many booleans"); - break; - case tt_UnrecognizedBoolean: - swprintf( ErrorString, L"Unrecognized boolean flag"); - break; - case tt_MissingClosingBracket: - swprintf( ErrorString, L"Missing closing bracket"); - break; - case tt_SLOOPArgumentBufferTooSmall: - swprintf( ErrorString, L"SLOOP number too big, the compiler cannot handle such a big number"); - break; - case tt_EmptyParameterList: - swprintf( ErrorString, L"Missing comma between parameters or empty parameter list"); - break; - case tt_UnableToParseArgument: - swprintf( ErrorString, L"Unable to parse argument"); - break; - case tt_MissingParameters: - swprintf( ErrorString, L"Missing parameters or missing comma between parameters "); - break; - case tt_PointNbOutOfRange: - swprintf( ErrorString, L"Point number out of range"); - break; - case tt_CVTIndexOutOfRange: - swprintf( ErrorString, L"CVT index out of range"); - break; - case tt_StorageIndexOutOfRange: - swprintf( ErrorString, L"Storage number out of range"); - break; - case tt_ContourNbOutOfRange: - swprintf( ErrorString, L"Contour number out of range"); - break; - case tt_FunctionNbOutOfRange: - swprintf( ErrorString, L"Function number out of range"); - break; - case tt_ArgumentOutOfRange: - swprintf( ErrorString, L"Argument number out of range"); - break; - case tt_ArgumentIndexOutOfRange: - swprintf( ErrorString, L"Compiler Error! Argument index out of range"); - break; - case tt_NotEnoughMemory: - swprintf( ErrorString, L"Not enough memory"); - break; - case tt_DeltaListMissing: - swprintf( ErrorString, L"Delta, parameter list missing"); - break; - case tt_DeltaOpeningParenthesisMissing: - swprintf( ErrorString, L"Delta, opening parenthesis missing"); - break; - case tt_DeltaClosingParenthesisMissing: - swprintf( ErrorString, L"Delta, closing parenthesis missing"); - break; - case tt_PointSizeOutOfRange: - swprintf( ErrorString, L"Delta, point size out of range"); - break; - case tt_DeltaDenominatorMissing: - swprintf( ErrorString, L"Delta, denominator missing, format should be eg: (19 @12 3/8)"); - break; - case tt_DeltaWrongDenominator: - swprintf( ErrorString, L"Delta, wrong denominator, format should be eg: (19 @12 3/8)"); - break; - case tt_DeltaAtSignMissing: - swprintf( ErrorString, L"Delta, @ sign missing, format should be eg: (19 @12 3/8)"); - break; - case tt_DeltaClosingBracketMissing: - swprintf( ErrorString, L"Delta, closing bracket missing"); - break; - case tt_TooManyDeltas: - swprintf( ErrorString, L"Delta, too many deltas in the same line"); - break; - case tt_DeltaOORangePpem: - swprintf( ErrorString, L"Delta, out of range ppem for Delta"); - break; - case tt_TooManyLabels: - swprintf( ErrorString, L"Too many labels in the same block"); - break; - case tt_LabelTooLong: - swprintf( ErrorString, L"Label too long, limited to %hd character",(short) (MAXLABELLENGTH-1)); - break; - case tt_DuplicateLabel: - swprintf( ErrorString, L"Same label used twice"); - break; - case tt_EndWithoutBegin: - swprintf( ErrorString, L"#END without corresponding #BEGIN"); - break; - case tt_MissingEndBlock: - swprintf( ErrorString, L"End(s) of block, #END, missing"); - break; - case tt_TooManyEnbeddedBlocks: - swprintf( ErrorString, L"Too many levels of nested blocks, limit = %hd",(short) (MAXBLOCKEMBEDDING-1)); - break; - case tt_CompositeCode: - swprintf( ErrorString, L"Composite commands mixed into TrueType code"); - break; - case tt_VoidLabel: - swprintf( ErrorString, L"NULL label, must have at least one character"); - break; - case tt_LabelNotFound: - swprintf( ErrorString, L"Corresponding label not found"); - break; - case tt_ExpectingAComma: - swprintf( ErrorString, L"#PUSH argument list, missing arguments or missing comma"); - break; - case tt_TooManyPushArgs: - swprintf( ErrorString, L"#PUSH, too many arguments, limit = %hd", (short)(PUSH_ARG)); - break; - case tt_ParseOverflow: - swprintf( ErrorString, L"Number too large to be parsed, larger than 32,767"); - break; - case tt_JRExpectingABracket: - swprintf( ErrorString, L"JR instruction in PushOff mode, expecting an opening bracket"); - break; - case tt_JRExpectingABWLabel: - swprintf( ErrorString, L"JR instruction in PushOff mode, expecting an Bn or a Wn label"); - break; - case tt_JRExpectingAEqual: - swprintf( ErrorString, L"JR instruction in PushOff mode, expecting an equal between labels"); - break; - case tt_JRExpectingALabel: - swprintf( ErrorString, L"JR instruction in PushOff mode, expecting a #label"); - break; - case tt_JumpTooBigForByte: - swprintf( ErrorString, L"#PUSH, Bn : jump too far to be a byte"); - break; - case tt_JumpNegativeForByte: - swprintf( ErrorString, L"#PUSH, Bn : negative jump cannot be a byte, use Wn"); - break; - case tt_EIFwithoutIF: - swprintf( ErrorString, L"EIF without IF"); - break; - case tt_ELSEwithoutIF: - swprintf( ErrorString, L"ELSE without IF"); - break; - case tt_ELSEwithinELSE: - swprintf( ErrorString, L"expecting a EIF"); - break; - case tt_TooManyEmbeddedIF: - swprintf( ErrorString, L"too many embedded IF"); - break; - case tt_ExpectingaBEGIN: - swprintf( ErrorString, L"expecting a #BEGIN after IF[], ELSE[], FDEF[] or IDEF[] in push on mode"); - break; - case tt_FDEFInsideFDEF: - swprintf( ErrorString, L"FDEF found within FDEF - ENDF pair"); - break; - case tt_FDEFInsideIDEF: - swprintf( ErrorString, L"FDEF found within IDEF - ENDF pair"); - break; - case tt_IDEFInsideFDEF: - swprintf( ErrorString, L"IDEF found within FDEF - ENDF pair"); - break; - case tt_IDEFInsideIDEF: - swprintf( ErrorString, L"IDEF found within IDEF - ENDF pair"); - break; - case tt_ENDFwithoutFDEF: - swprintf( ErrorString, L"ENDF found without corresponding FDEF or IDEF"); - break; - case tt_IFwithoutEIF: - swprintf( ErrorString, L"IF without corresponding EIF"); - break; - case tt_FDEFwithoutENDF: - swprintf( ErrorString, L"FDEF without corresponding ENDF"); - break; - case tt_IDEFwithoutENDF: - swprintf( ErrorString, L"IDEF without corresponding ENDF"); - break; - - case tt_PUSHONwhenAlreadyOn: - swprintf( ErrorString, L"#PUSHON when already in push on mode"); - break; - case tt_PUSHOFFwhenAlreadyOff: - swprintf( ErrorString, L"#PUSHOFF when already in push off mode"); - break; - case tt_IFgoingAcrossBlocks: - swprintf( ErrorString, L"IF statement going across block (#BEGIN #END) boundaries"); - break; - case tt_FDEFgoingAcrossBlocks: - swprintf( ErrorString, L"FDEF statement going across block (#BEGIN #END) boundaries"); - break; - case tt_IDEFgoingAcrossBlocks: - swprintf( ErrorString, L"IDEF statement going across block (#BEGIN #END) boundaries"); - break; - - case tt_IDEF_FDEFinGlyphProgram: - swprintf( ErrorString, L"FDEF and IDEF can be called only from font program or the pre-program"); - break; - - case tt_INSTCTRLnotInPreProgram: - swprintf( ErrorString, L"INSTCTRL[] can only be called from the pre-program"); - break; - case tt_ProgramTooBig: - swprintf( ErrorString, L"Program too big, if you really need such a big program, call product support"); - break; - case tt_TooManyArguments: - swprintf( ErrorString, L"Program too big (too many arguments), if you really need such a big program, call product support"); - break; - case tt_DELTAWithoutArguments: - swprintf( ErrorString, L"DELTA without argument in PUSHON mode"); - break; - case tt_DELTAWithArguments: - swprintf( ErrorString, L"DELTA with arguments in PUSHOFF mode"); - break; - case tt_PUSHBWInPushON: - swprintf( ErrorString, L"Invalid use of PUSHB or PUSHW in PUSHON mode, use #PUSH instead"); - break; - case tt_WildCardInPush: - swprintf( ErrorString, L"Invalid use * in a PUSH instruction"); - break; - - case tt_NotImplemented: - swprintf( ErrorString, L"Not implemented"); - break; - - default : - swprintf( ErrorString, L"Unknown error!"); - break; - } - -} - - -typedef struct { - const wchar_t *name; /* Apple instruction name */ - const wchar_t *description; /* descriptive info */ - const wchar_t *pops; /* What the instruction pops */ - const wchar_t *booleans; /* booleans */ -} co_InstructionType; - - -typedef struct { - wchar_t type; - wchar_t Reserved1; /* for struct alignment */ - wchar_t Reserved2; - wchar_t Reserved3; - const wchar_t *explanation; - short lowestValidValue; /* 6-7-90 JTS Range Checking Adds */ - short highestValidValue; /* 6-7-90 JTS Range Checking Adds */ -} co_ParameterDescriptionType; - -/* lower case is reserved for loop variable dependent pops */ -co_ParameterDescriptionType co_ppDescription[] = { - { L'G',L' ',L' ',L' ', L"Glyph Index" ,0 ,(short)USHORTMAX}, // allow full unicode range for glyph index B.St. - { L'O',L' ',L' ',L' ', L"Offset " ,SHORTMIN ,SHORTMAX}, - { L'P',L' ',L' ',L' ', L"Point Number" ,0 ,SHORTMAX}, - { L'M',L' ',L' ',L' ', L"Matrix elements, can be either integer or real numbers" ,0,0} -}; - -#define NumberOfParameterDescrEntries 4 - -const co_InstructionType co_instruction[] = { -/**** Instruction name, descriptive info, pops, booleans *****/ - { L"OFFSET", L"Component character", L"GOO", L"R" }, - { L"SOFFSET", L"Component character", L"GOOMMMM", L"R" }, - { L"ANCHOR", L"Component character", L"GPP", L"" }, - { L"SANCHOR", L"Component character", L"GPPMMMM", L"" }, - { L"OVERLAP", L"Component character", L"", L"" }, - { L"NONOVERLAP", L"Component character", L"", L"" }, - { L"USEMYMETRICS", L"Component character", L"", L"" }, - { L"SCALEDCOMPONENTOFFSET", L"Component character", L"", L"" }, - { L"UNSCALEDCOMPONENTOFFSET", L"Component character", L"", L"" } -}; - -typedef struct { - wchar_t type; - wchar_t code; - char result; -} co_BooleanTranslationType; - -const co_BooleanTranslationType co_booleanTranslation[] = { - { L'R', L'R', 1 }, - { L'R', L'r', 0 } -}; - -#define co_NumberOfBooleanTranslations (sizeof(co_booleanTranslation) / sizeof(co_BooleanTranslationType)) - - -#define CONUMBEROFINSTRUCTIONS (sizeof(co_instruction) / sizeof(co_InstructionType)) - - -wchar_t *CO_Parse2_14Number( wchar_t *p, wchar_t *endP,short *Number, short * error ); -wchar_t *CO_Parse2_14Number( wchar_t *p, wchar_t *endP,short *Number, short * error ) -{ - short i, negatif; - int32_t tempNumber; - - /* skip white space */ - while ( *p == L' ' && p < endP) - p++; - - tempNumber = 0; - negatif = false; - i = 0; - - if ( *p == L'-' && p < endP ) - { - negatif = true; - p++; - } - - if ( *p == L'+' && p < endP && !negatif) - /* we accept + or - but don't want -+ together */ - { - p++; - } - - /* read the first number */ - if ( *p >= L'0' && *p <= L'9' && p < endP ) - { - if ( *p > L'2') { - *error = tt_ParseOverflow; - return p; - } - tempNumber = tempNumber * 10 + (short) *p - (short) '0'; - p++; - i++; - } else { - *error = tt_UnableToParseArgument; - return p; - } - - - if ( *p >= L'0' && *p <= L'9' && p < endP ) - { - *error = tt_ParseOverflow; - return p; - } - - if ( *p == L'.' && p < endP ) - { - p++; - /* read the fractional part */ - while ( *p >= L'0' && *p <= L'9' && p < endP ) - { - if (i < 5) /* ignore the remainding digits */ - { - tempNumber = tempNumber * 10 + (short) *p - (short) '0'; - i++; - } - p++; - } - } - - while (i < 5) - { - tempNumber = tempNumber * 10; - i++; - } - - if (i == 0) *error = tt_UnableToParseArgument; - - - - tempNumber = tempNumber * 0x4000 / 10000; - if (negatif) tempNumber = - tempNumber; - - if (tempNumber > SHORTMAX) - { - tempNumber = SHORTMAX; - } - - if (tempNumber < SHORTMIN) - { - tempNumber = SHORTMIN; - } - - *Number = (short)tempNumber; - return p; - -} - -wchar_t * CO_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, - co_ParameterDescriptionType co_ppDescription[], short *argc,short *args, - int32_t * SelectionLength, short * co_error); -wchar_t * CO_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, - co_ParameterDescriptionType co_ppDescription[], short *argc,short *args, - int32_t * SelectionLength, short * co_error) -{ - short argindex, argNb; - wchar_t * tempP; - // const wchar_t * argTypeBuffer = co_instruction[InstructionIndex].pops; - *argc = 0; - argindex = 0; - bool inRange; - - argNb = (short)STRLENW(co_instruction[InstructionIndex].pops); - - for (argindex=0;argindex= EOLPtr) break; - - if ( *CurrentPtr++ != L',' ) - { - *co_error = co_EmptyParameterList; - return (CurrentPtr-1); - } - - /* skip extra spaces */ - while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) - CurrentPtr++; - - if (CurrentPtr >= EOLPtr) break; - - if ( co_instruction[InstructionIndex].pops[argindex] == L'M' ) - { - /* there should be a 2.14 number */ - tempP = CurrentPtr; - CurrentPtr = CO_Parse2_14Number(CurrentPtr, EOLPtr, &args[argindex], co_error); - *argc = *argc + 1; - if (*co_error != co_NoError) return CurrentPtr; - } else { - short foundPP, PPindex; - - /* there should be a number */ - tempP = CurrentPtr; - CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &args[argindex], SelectionLength, co_error); - if (*co_error != co_NoError) return CurrentPtr; - - foundPP = 0; /* Have not found it yet... */ - for (PPindex=0;(PPindex= EOLPtr) - { - *co_error = co_BooleanFlagsMissing; - *Selectionlength = 1; - return CurrentPtr; - }; - tempP = CurrentPtr; - CurrentPtr ++; - NBOfBooleans = (short)STRLENW(co_instruction[InstructionIndex].booleans) ; - booleanShift = NBOfBooleans; - - for (booleanCount = 0; - // for balance L'[' - booleanCount < NBOfBooleans && *CurrentPtr != L']' && CurrentPtr < EOLPtr ; - booleanCount++ ) - { - booleanShift--; - for ( found = k = 0; k < (short)co_NumberOfBooleanTranslations; k++ ) { - if ( co_booleanTranslation[k].type == co_instruction[InstructionIndex].booleans[booleanCount] && - (co_booleanTranslation[k].code) == *CurrentPtr ) - { - found = 1; - *RoundingCode = co_booleanTranslation[k].result; - } - } - if (!found) - { - *co_error = co_UnrecognizedBoolean; - *Selectionlength = 1; - return CurrentPtr; - } - - CurrentPtr++; - } - - if (booleanCount != NBOfBooleans) - { - *co_error = co_WrongNumberOfBoolean; - *Selectionlength = (short)(CurrentPtr-tempP)+1; - return tempP; - }; - - if ( (CurrentPtr >= EOLPtr) || (*CurrentPtr == L',' ) ) - { - *co_error = co_MissingClosingBracket; - *Selectionlength = (short)(CurrentPtr-tempP); - return tempP; - }; - - if ( /* [ for balance */ *CurrentPtr != L']' ) - { - *co_error = co_TooManyBooleans; - *Selectionlength = (short)(CurrentPtr-tempP)+1; - return tempP; - }; - CurrentPtr ++; - return CurrentPtr; -} - -/* - * returns true on error compile the composite information - */ - -wchar_t *CO_Compile(TrueTypeFont * font, TrueTypeGlyph * glyph, wchar_t *StartPtr, wchar_t * EndPtr, short *numCompositeContours, short *numCompositePoints, int32_t * SelectionLength, short * co_error); -wchar_t *CO_Compile(TrueTypeFont * font, TrueTypeGlyph * glyph, wchar_t *StartPtr, wchar_t * EndPtr, short *numCompositeContours, short *numCompositePoints, int32_t * SelectionLength, short * co_error) { -// B.St.'s DISCLAIMER: So far, I've barely done a minimum to make this hideous piece of code somewhat understandable. -// The way this appears to work is by looking at a pair of composite instructions (prevInstrIndex and currInstrIndex) -// in a window that is being slided along the code. This appears to be used to determine, whether such instructions -// as USEMYMETRICS or OVERLAP and NONOVERLAP (the latter two being obsolete, as far as I understand the TT manual), -// are properly followed by further composite instructions... - short LineNb,LastLineCompiled,RoundingCode = 0,currInstrIndex = 0,prevInstrIndex = 0,args[256],argc = 0; - bool currInstrIsCompInstr,prevInstrIsCompInstr; - int32_t LineLength,StringLength; - wchar_t *CurrentPtr,*EOLPtr; - TTCompositeProfile compositeProfile; - sfnt_glyphbbox Newbbox; // for composite glyph - - Newbbox.xmin = SHRT_MAX; - Newbbox.ymin = SHRT_MAX; - Newbbox.xmax = SHRT_MIN; - Newbbox.ymax = SHRT_MIN; - - *SelectionLength = 0; - *co_error = co_NoError; - prevInstrIsCompInstr = false; - - glyph->componentSize = 0; - compositeProfile.GlobalUSEMYMETRICS = 0; - compositeProfile.GlobalSCALEDCOMPONENTOFFSET = 0; - compositeProfile.GlobalUNSCALEDCOMPONENTOFFSET = 0; - compositeProfile.GlobalNON_OVERLAPPING = 1; - compositeProfile.GlobalMORE_COMPONENTS = 1; - compositeProfile.nextExitOffset = false; - - compositeProfile.numberOfCompositeContours = 0; - compositeProfile.numberOfCompositePoints = 0; - compositeProfile.numberOfCompositeElements = 0; - - compositeProfile.anchorPrevPoints = -1; - - CurrentPtr = StartPtr; - LastLineCompiled = 0; - - for ( LineNb = 1; CurrentPtr <= EndPtr && *co_error == co_NoError; ) { - CurrentPtr = TT_SkipCommentsAndEmptyLines( CurrentPtr,EndPtr, &LineNb, co_error ); - if (*co_error != co_NoError) { *SelectionLength = 2; goto failure; } - - if (CurrentPtr == EndPtr) break; - - LineLength = TT_GetLineLength(CurrentPtr, EndPtr); - - if (LineNb == LastLineCompiled) { *co_error = co_TwoInstructionsInSameLine; *SelectionLength = LineLength; goto failure; } - LastLineCompiled = LineNb; - - // regular instruction - StringLength = TT_GetStringLength (CurrentPtr, EndPtr); - currInstrIndex = 0; - while (currInstrIndex < (short)CONUMBEROFINSTRUCTIONS && !(StringLength == (int32_t)STRLENW(co_instruction[currInstrIndex].name) && wcsncmp(CurrentPtr, co_instruction[currInstrIndex].name, StringLength) == 0)) currInstrIndex++; - currInstrIsCompInstr = currInstrIndex < (short)CONUMBEROFINSTRUCTIONS; - - if (prevInstrIsCompInstr) { - font->UpdateCompositeProfile(glyph,&compositeProfile,currInstrIsCompInstr ? CO_CompInstrFollow : CO_StdInstrFollow, RoundingCode, prevInstrIndex, args, argc, &Newbbox, co_error); - } - - if (!currInstrIsCompInstr) goto success; // because it is assumed to be the first TrueType instruction - - EOLPtr = CurrentPtr + LineLength; - prevInstrIndex = currInstrIndex; - CurrentPtr = CO_ReadInstructionBooleans (CurrentPtr+StringLength, EOLPtr, currInstrIndex, &RoundingCode, SelectionLength, co_error); - if (*co_error != co_NoError) goto failure; - - CurrentPtr = CO_ReadInstructionParameters (CurrentPtr, EOLPtr, currInstrIndex, co_ppDescription, &argc, args, SelectionLength, co_error); - if (*co_error != co_NoError) goto failure; - - prevInstrIsCompInstr = true; - } - - if (prevInstrIsCompInstr) { - font->UpdateCompositeProfile(glyph,&compositeProfile,CurrentPtr >= EndPtr ? CO_NothingFollows : CO_StdInstrFollow, RoundingCode, currInstrIndex, args, argc,&Newbbox,co_error); - if (*co_error != co_NoError) goto failure; - } - -success: - if (Newbbox.xmin != SHRT_MAX) { // update the bounding box of a composite glyph - glyph->xmin = Newbbox.xmin; - glyph->ymin = Newbbox.ymin; - glyph->xmax = Newbbox.xmax; - glyph->ymax = Newbbox.ymax; - } - *numCompositeContours = compositeProfile.numberOfCompositeContours; - *numCompositePoints = compositeProfile.numberOfCompositePoints; - return CurrentPtr; - -failure: - glyph->componentSize = 0; - *numCompositeContours = 0; - *numCompositePoints = 0; - return CurrentPtr; -} // CO_Compile - - -void CO_GetErrorString (short ErrorNb, wchar_t * ErrorString); -void CO_GetErrorString (short ErrorNb, wchar_t * ErrorString) -{ - - switch (ErrorNb ) { - case co_NoError: - swprintf( ErrorString, L"There is no Error"); - break; - case tt_EmbeddedComment: - swprintf( ErrorString, L"Embedded comment"); - break; - case tt_UnterminatedComment: - swprintf( ErrorString, L"Unterminated comment"); - break; - case co_TwoInstructionsInSameLine: - swprintf( ErrorString, L"End of line expected"); - break; - case co_BooleanFlagsMissing: - swprintf( ErrorString, L"bool flags missing"); - break; - case co_WrongNumberOfBoolean: - swprintf( ErrorString, L"Wrong number of boolean flags"); - break; - case co_TooManyBooleans: - swprintf( ErrorString, L"Too many booleans"); - break; - case co_UnrecognizedBoolean: - swprintf( ErrorString, L"Unrecognized boolean flag"); - break; - case co_MissingClosingBracket: - swprintf( ErrorString, L"Missing closing bracket"); - break; - case co_EmptyParameterList: - swprintf( ErrorString, L"Missing comma between parameters or empty parameter list"); - break; - case co_MissingParameters: - swprintf( ErrorString, L"Missing parameters"); - break; - case co_PointNbOutOfRange: - swprintf( ErrorString, L"Point number out of range"); - break; - case co_GlyphIndexOutOfRange: - swprintf( ErrorString, L"Glyph index out of range"); - break; - case co_ArgumentOutOfRange: - swprintf( ErrorString, L"Argument out of range"); - break; - case tt_ParseOverflow: - swprintf( ErrorString, L"Number too big to be parsed, bigger than MaxShort"); - break; - case co_AnchorArgExceedMax: - swprintf( ErrorString, L"Anchor argument exceed maximum value"); - break; - case co_AnchorNothingAbove: - swprintf( ErrorString, L"Composite, no instruction in the line above"); - break; - case co_2_14Overflow: - swprintf( ErrorString, L"Composite, number too big for 2.14 float"); - break; - case co_ComponentSizeOverflow: - swprintf( ErrorString, L"Composite, too many components"); - break; - - case co_OverlapLastInstruction: - swprintf( ErrorString, L"Composite, OVERLAP cannot be the last composite command"); - break; - case co_NonOverlapLastInstruction: - swprintf( ErrorString, L"Composite, NONOVERLAP cannot be the last composite command"); - break; - case co_UseMymetricsLastInstruction: - swprintf( ErrorString, L"Composite, USEMYMETRICS cannot be the last composite command"); - break; - case co_ScaledComponentOffsetLastInstruction: - swprintf( ErrorString, L"Composite, SCALEDCOMPONENTOFFSET cannot be the last composite command"); - break; - case co_UnscaledComponentOffsetLastInstruction: - swprintf( ErrorString, L"Composite, UNSCALEDCOMPONENTOFFSET cannot be the last composite command"); - break; - case co_ScaledComponentOffsetAlreadySet: - swprintf( ErrorString, L"Composite, UNSCALEDCOMPONENTOFFSET (Microsoft compatible) cannot be the used when SCALEDCOMPONENTOFFSET (Apple compatible) is alread used. Only one can be used."); - break; - case co_UnscaledComponentOffsetAlreadySet: - swprintf( ErrorString, L"Composite, SCALEDCOMPONENTOFFSET (Apple compatible) cannot be the used when UNSCALEDCOMPONENTOFFSET (Microsoft compatible) is alread used. Only one can be used."); - break; - case co_ComponentChangeOnVariationFont: - swprintf(ErrorString, L"Composite definition has changed or is not present"); - break; - - case co_NotImplemented: - swprintf( ErrorString, L"Not implemented"); - break; - default : - swprintf( ErrorString, L"Unknown error!"); - break; - } - -} - -bool DisassemComponent(TrueTypeGlyph *glyph, TextBuffer *src, wchar_t errMsg[]) { - short i,flags,glyphIndex,arg1,arg2,xscale = 0,yscale = 0,scale01 = 0,scale10 = 0; - wchar_t c,buf[maxLineSize]; - - i = 0; - do { - flags = glyph->componentData[i++]; - flags = SWAPW(flags); - glyphIndex = glyph->componentData[i++]; - glyphIndex = SWAPW(glyphIndex); - if (flags & ARG_1_AND_2_ARE_WORDS) { - arg1 = glyph->componentData[i++]; - arg1 = SWAPW(arg1); - arg2 = glyph->componentData[i++]; - arg2 = SWAPW(arg2); - } else if (flags & ARGS_ARE_XY_VALUES ) { // signed bytes - signed char *sByte = (signed char *)&glyph->componentData[i]; - arg1 = sByte[0]; - arg2 = sByte[1]; - i++; - } else { // unsigned bytes, do we ever have those? - unsigned char *uByte = (unsigned char *)&glyph->componentData[i]; - arg1 = uByte[0]; - arg2 = uByte[1]; - i++; - } - - if (flags & WE_HAVE_A_TWO_BY_TWO) { - xscale = glyph->componentData[i++]; - xscale = (signed short)SWAPW(xscale); - scale01 = glyph->componentData[i++]; - scale01 = (signed short)SWAPW(scale01); - scale10 = glyph->componentData[i++]; - scale10 = (signed short)SWAPW(scale10); - yscale = glyph->componentData[i++]; - yscale = (signed short)SWAPW(yscale); - } else if (flags & WE_HAVE_A_SCALE) { - short word = glyph->componentData[i++]; - xscale = yscale = SWAPW(word); - scale01 = scale10 = 0; - flags = (flags & ~WE_HAVE_A_SCALE) | WE_HAVE_A_TWO_BY_TWO; - } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { - xscale = glyph->componentData[i++]; - xscale = (signed short)SWAPW(xscale); - yscale = glyph->componentData[i++]; - yscale = (signed short)SWAPW(yscale); - scale01 = scale10 = 0; - flags = (flags & ~WE_HAVE_AN_X_AND_Y_SCALE) | WE_HAVE_A_TWO_BY_TWO; - } - - if (i > glyph->componentSize) { - swprintf(errMsg,L"DisassemComponent: Component data size %hd does not match parsed %hd size", glyph->componentSize,i); - return false; - } - - if (flags & USE_MY_METRICS) src->Append(L"USEMYMETRICS[]\r"); - else if (!(flags & NON_OVERLAPPING)) src->Append(L"OVERLAP[]\r"); - // If both UNSCALED_COMPONENT_OFFSET and SCALED_COMPONENT_OFFSET are set we will only emit the UNSCALEDCOMPONENTOFFSET for Microsoft compatibility. These are mutually exclusive flags. - if (flags & UNSCALED_COMPONENT_OFFSET) src->Append(L"UNSCALEDCOMPONENTOFFSET[]\r"); else if (flags & SCALED_COMPONENT_OFFSET) src->Append(L"SCALEDCOMPONENTOFFSET[]\r"); - c = flags & ROUND_XY_TO_GRID ? L'R' : L'r'; - - if (flags & WE_HAVE_A_TWO_BY_TWO) { - if (flags & ARGS_ARE_XY_VALUES) swprintf(buf,L"SOFFSET[%c",c); else swprintf(buf,L"SANCHOR["); - swprintf(&buf[STRLENW(buf)],L"], %hu, %hd, %hd, %.4f, %.4f, %.4f, %.4f\r", - glyphIndex,arg1,arg2, - (double)xscale /16384.0 + 0.000005, (double)scale01/16384.0 + 0.000005, - (double)scale10/16384.0 + 0.000005, (double)yscale /16384.0 + 0.000005); - } else { - if (flags & ARGS_ARE_XY_VALUES) swprintf(buf,L"OFFSET[%c",c); else swprintf(buf,L"ANCHOR["); - swprintf(&buf[STRLENW(buf)],L"], %hu, %hd, %hd\r",glyphIndex,arg1,arg2); - } - src->Append(buf); - } while (flags & MORE_COMPONENTS); - - return true; -} // DisassemComponent - -bool TTAssemble(ASMType asmType, TextBuffer* src, TrueTypeFont* font, TrueTypeGlyph* glyph, - int32_t maxBinLen, unsigned char* bin, int32_t* actBinLen, bool variationCompositeGuard, int32_t* errPos, int32_t* errLen, wchar_t errMsg[]) { - - wchar_t* startPtr, * endPtr, * SelStartPtr, * tempPtr; - short BinaryOffset, CompileError = co_NoError, StackNeed, MaxFunctionDefs, ErrorLineNb, componentSize, numCompositeContours, numCompositePoints, maxContourNumber, maxPointNumber; - int32_t srcLen, highestCvtNum; - sfnt_maxProfileTable profile; - short componentData[MAXCOMPONENTSIZE]; - - tt_flashingPoints MyflashingPoints; - - profile = font->GetProfile(); - highestCvtNum = font->TheCvt()->HighestCvtNum(); - - *actBinLen = 0; *errPos = -1; *errLen = 0; - - if (asmType == asmGLYF && glyph->composite && src->TheLength() == 0 && !DisassemComponent(glyph, src, errMsg)) return false; - - srcLen = src->TheLength(); - startPtr = (wchar_t*)NewP((srcLen + 1L) * sizeof(wchar_t)); // if lengths are zero, we are guaranteed a pointer if + 1L - - SelStartPtr = startPtr; - size_t len; - src->GetText(&len, startPtr); - tempPtr = startPtr; - endPtr = startPtr + srcLen; - - switch (asmType) { - case asmFPGM: - TT_SetRangeCheck(0, 0, profile.maxElements - 1, 0, (short)highestCvtNum, profile.maxStorage - 1); - break; - case asmPREP: - /* twilight point may be used in the pre-program for italic slope,... */ - TT_SetRangeCheck(0, profile.maxTwilightPoints, profile.maxElements - 1, profile.maxFunctionDefs - 1, (short)highestCvtNum, profile.maxStorage - 1); - break; - case asmGLYF: - /* compile first the composite information */ - componentSize = glyph->componentSize; - memcpy(componentData, glyph->componentData, glyph->componentSize * sizeof(short)); - tempPtr = CO_Compile(font, glyph, tempPtr, endPtr, &numCompositeContours, &numCompositePoints, errLen, &CompileError); - if (CompileError == co_NoError) - { - // If a variation font check to make sure new component size and data matches pre CO_Compile() state. - if (font->IsVariationFont() && variationCompositeGuard && (glyph->componentSize > 0 || componentSize > 0)) - { - const CheckCompositeResult result = CheckCompositeVariationCompatible(componentData, componentSize * sizeof(short), glyph->componentData, glyph->componentSize * sizeof(short)); - if (result == CheckCompositeResult::Fail) - { - // VTT should not change definition of glyphs in a variation font. - CompileError = co_ComponentChangeOnVariationFont; - // Revert - glyph->componentSize = componentSize; - memcpy(glyph->componentData, componentData, componentSize * sizeof(short)); - - } - else if (result == CheckCompositeResult::Tolerance) - { - // VTT should not change definition of glyphs in a variation font. - // Revert but no error - glyph->componentSize = componentSize; - memcpy(glyph->componentData, componentData, componentSize * sizeof(short)); - } - } - } - if (CompileError == co_NoError) { - //if (componentSize == 0 && glyph->componentSize > 0) { // we've just created a new composite - if(glyph->componentSize > 0) { - maxContourNumber = numCompositeContours; maxPointNumber = numCompositePoints - 1 + PHANTOMPOINTS; // hence pick up-to-date profile data for range ends - } - else { - maxContourNumber = (short)glyph->numContoursInGlyph - 1; maxPointNumber = glyph->endPoint[maxContourNumber] + PHANTOMPOINTS; // else glyph data should still be up-to-date - } - TT_SetRangeCheck(maxContourNumber, maxPointNumber, profile.maxElements - 1, profile.maxFunctionDefs - 1, (short)highestCvtNum, profile.maxStorage - 1); - } - else { - *errPos = (int32_t)(tempPtr - startPtr); - if (*errLen < 1) *errLen = 1; // it is easy to find a selection than a cursor <--- and difficult to get it right in the first place??? - CO_GetErrorString(CompileError, errMsg); - } - break; - } - - if (CompileError == co_NoError) { - - StackNeed = 0; - MaxFunctionDefs = 0; - - tempPtr = TT_Compile(tempPtr, endPtr, SelStartPtr, bin, maxBinLen, actBinLen, - &BinaryOffset, errLen, &ErrorLineNb, &StackNeed, &MaxFunctionDefs, &MyflashingPoints, asmType, &CompileError); - - if (CompileError == tt_NoError) { - font->UpdateAssemblerProfile(asmType, MaxFunctionDefs + 1, StackNeed, (short)(*actBinLen)); - } - else { - *errPos = (int32_t)(tempPtr - startPtr); - if (*errLen < 1) *errLen = 1; // it is easy to find a selection than a cursor <--- and difficult to get it right in the first place??? - TT_GetErrorString(CompileError, errMsg); - - *actBinLen = 0; - if (asmType == asmGLYF) glyph->componentSize = 0; - } - } - - font->UpdateGlyphProfile(glyph); - DisposeP((void**)&startPtr); - - return CompileError == tt_NoError; - -} +/***** + * + * TTAssembler.c + * + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * + * Notes on TrueType Assembler GregH 12/2011 + * Instruction matching is a linear search. Should be easy to move to a binary search + * Numbers can be hexadecimal when prefixed by a 0x (not 0X). Hex digits can be upper or lower case + * Maximum number is 0xFFFF if positive or 0x8FFF if negative + * + or - can prefix numbers + * 256 (PUSH_ARG) is the maximum number of arguments for one #PUSH + * A label must be less than 22 (MAXLABELLENGTH) characters + * 200 (MAXJRPAIR) total PUSHs in a block + * + *****/ +#define _CRT_SECURE_NO_DEPRECATE + +#include /* for swprintf */ +#include /* for wcslen */ +#include /* for SHRT_MIN MAX */ +#include "pch.h" + +#define SHORTMAX 32767 +#define SHORTMIN -32768 +#define USHORTMAX 65535 + +#define MAXINSTRUCTIONCOUNT 16000 +#define MAXARGUMENTS (0x8000L - 4L) // don't push the limit +#define ARGTYPEBUFFER_SIZE 300 + +/* maximumembeding of #BEGIN #END block */ +#define MAXBLOCKEMBEDDING 6 + +/* ClaudeBe for the DELTA command, DeltaBase is constant 9 and DeltaShift is constant 3, + use the DLT command with relative ppem (0..15) and relative offset (-8..8) if you need + a specific DeltaBase or DeltaShift */ +#define DELTABASE 9 + +#define MAXIFRECURSION 20 + +/* maximal number of delta in one command */ +#define MAXDELTA 256 + +/* for the DovMan partial compilation feature that flash points referenced by the current command */ +#define tt_MAXFLASHINGPOINTS 4 + +#define tt_MAXINSTRUCTIONS 8000 /* size needed for the chicago Japanese fonts */ + +typedef struct { + unsigned short flashingPoints[tt_MAXFLASHINGPOINTS]; + short NumberOfFlashingPoint; +} tt_flashingPoints; + +#define tt_NoError 0 +#define tt_EmbeddedComment 1 +#define tt_UnterminatedComment 2 +#define tt_UnknownSwitch 3 +#define tt_UnknownInstruction 4 +#define tt_TwoInstructionsInSameLine 5 /* could mean garbage, too many arguments,... */ +#define tt_BooleanFlagsMissing 6 +#define tt_WrongNumberOfBoolean 7 +#define tt_TooManyBooleans 8 +#define tt_UnrecognizedBoolean 9 +#define tt_MissingClosingBracket 10 +#define tt_SLOOPArgumentBufferTooSmall 11 /* reduce the count for SLOOP, the compiler cannot handle such a big number */ +#define tt_EmptyParameterList 12 /* missing comma between parameters or empty parameter list */ +#define tt_UnableToParseArgument 13 +#define tt_MissingParameters 14 +#define tt_PointNbOutOfRange 15 +#define tt_CVTIndexOutOfRange 16 +#define tt_StorageIndexOutOfRange 17 +#define tt_ContourNbOutOfRange 18 +#define tt_FunctionNbOutOfRange 19 +#define tt_ArgumentOutOfRange 20 +#define tt_ArgumentIndexOutOfRange 21 /* this error should never happend, this comes from a programming error */ +#define tt_NotEnoughMemory 22 +#define tt_DeltaListMissing 23 +#define tt_DeltaOpeningParenthesisMissing 24 +#define tt_PointSizeOutOfRange 25 +#define tt_DeltaDenominatorMissing 26 +#define tt_DeltaWrongDenominator 27 +#define tt_DeltaAtSignMissing 28 +#define tt_TooManyDeltas 29 +#define tt_DeltaClosingBracketMissing 30 +#define tt_DeltaOORangePpem 31 +#define tt_TooManyLabels 32 +#define tt_LabelTooLong 33 +#define tt_DuplicateLabel 34 +#define tt_EndWithoutBegin 35 +#define tt_MissingEndBlock 36 +#define tt_TooManyEnbeddedBlocks 37 +#define tt_CompositeCode 38 +#define tt_VoidLabel 39 +#define tt_LabelNotFound 40 +#define tt_ExpectingAComma 41 +#define tt_TooManyPushArgs 42 +#define tt_ParseOverflow 44 +#define tt_JRExpectingABracket 45 +#define tt_JRExpectingABWLabel 46 +#define tt_JRExpectingAEqual 47 +#define tt_JRExpectingALabel 48 + +#define tt_JumpTooBigForByte 49 + +#define tt_EIFwithoutIF 50 +#define tt_ELSEwithoutIF 51 +#define tt_ELSEwithinELSE 52 +#define tt_TooManyEmbeddedIF 53 +#define tt_ExpectingaBEGIN 54 + +#define tt_FDEFInsideFDEF 55 +#define tt_ENDFwithoutFDEF 56 + +#define tt_IFwithoutEIF 57 +#define tt_FDEFwithoutENDF 58 +#define tt_IDEFwithoutENDF 59 + +#define tt_PUSHONwhenAlreadyOn 60 +#define tt_PUSHOFFwhenAlreadyOff 61 +#define tt_IFgoingAcrossBlocks 62 +#define tt_FDEFgoingAcrossBlocks 63 +#define tt_IDEFgoingAcrossBlocks 64 + +#define tt_FDEFInsideIDEF 65 +#define tt_IDEFInsideFDEF 66 +#define tt_IDEFInsideIDEF 67 +#define tt_IDEF_FDEFinGlyphProgram 68 +#define tt_INSTCTRLnotInPreProgram 69 +#define tt_ProgramTooBig 70 +#define tt_TooManyArguments 71 +#define tt_DELTAWithoutArguments 72 + +#define tt_DeltaClosingParenthesisMissing 73 + +#define tt_DELTAWithArguments 74 + +#define tt_PUSHBWInPushON 75 +#define tt_WildCardInPush 76 +#define tt_JumpNegativeForByte 77 + +#define tt_NotImplemented 9999 +#define tt_Push_Switch 1 +#define tt_PushOn_Switch 2 +#define tt_PushOff_Switch 3 +#define tt_Begin_Switch 4 +#define tt_End_Switch 5 +#define tt_GHTBlockBegin_Switch 6 +#define tt_GHTBlockEnd_Switch 7 + +#define co_NoError 0 + +/* start at 100 for the composite errors to avoid overlapping */ + +#define co_TwoInstructionsInSameLine 101 +#define co_BooleanFlagsMissing 102 +#define co_UnrecognizedBoolean 103 +#define co_WrongNumberOfBoolean 104 +#define co_MissingClosingBracket 105 +#define co_TooManyBooleans 106 + +#define co_EmptyParameterList 107 +#define co_PointNbOutOfRange 108 +#define co_GlyphIndexOutOfRange 109 +#define co_ArgumentOutOfRange 110 +#define co_MissingParameters 111 + +#define co_2_14Overflow 114 + +#define co_NotImplemented 9999 + +typedef struct { + short WeAreInsideAnIF; + short NumberOfEmbeddedIF; /* for #PUSHON mode */ + short WeAreInsideAnFDEF; + short WeAreInsideAnIDEF; + short WeAreInPushOnMode; + short WeAreInPrePushMode; + short WeAreInsideGHBlock; + short LastCommandWasAnIF; /* used to check for #BEGIN (beginning of a block) */ + short LastCommandWasAnELSE; /* used to check for #BEGIN (beginning of a block) */ + short LastCommandWasAnFDEF; /* used to check for #BEGIN (beginning of a block) */ + short LastCommandWasAnIDEF; /* used to check for #BEGIN (beginning of a block) */ + short LastCommandWasAnJUMP; /* used to check for #BEGIN (beginning of a block) */ + short LastCommandWasAnEND; /* to check if the ENDF, ELSE or EIF is preceeded by #END (end of block) */ + short ELSEStatus[MAXIFRECURSION]; /* to keep track in embedded IF statement + if we are already in the ELSE clause */ +} tt_CompilationStatus; + + +void TT_memSwap( char *a,char *b,char *tmp,int32_t len ); +void TT_memSwap( char *a,char *b,char *tmp,int32_t len ) +{ + memcpy( tmp, a, len ); + memcpy( a, b, len ); + memcpy( b, tmp, len ); +} + + +typedef struct { + unsigned short point; + unsigned short relppem; + short num; /* numerator */ + unsigned short denum; /* denominator */ +} tt_deltaPType; + + +typedef struct { + const wchar_t *name; /* compiler switch string */ + const wchar_t *description; /* descriptive info */ + short index; /* switch index */ +} tt_CompilerSwitchType; + + +typedef struct { + wchar_t type; + wchar_t code; + wchar_t result; +} asm_BooleanTranslationType; + +typedef struct { + wchar_t type; + wchar_t Reserved1; /* for struct alignment */ + wchar_t Reserved2; + wchar_t Reserved3; + const wchar_t *explanation; + short lowestValidValue; /* 6-7-90 JTS Range Checking Adds */ + short highestValidValue; /* 6-7-90 JTS Range Checking Adds */ +} asm_PushAndPopDescriptionType; + +/* lower case is reserved for loop variable dependent pops */ +asm_PushAndPopDescriptionType asm_ppDescription1[] = { + { L'P',L' ',L' ',L' ', L"Point Number" ,0 ,0}, + { L'E',L' ',L' ',L' ', L"Zone Pointer" ,0 ,0}, /* This is handled differently */ + { L'D',L' ',L' ',L' ', L"Distance in pixels, I pixel = 64 units" ,SHORTMIN ,SHORTMAX}, + { L'B',L' ',L' ',L' ', L"bool, true or false" ,SHORTMIN ,SHORTMAX}, + { L'R',L' ',L' ',L' ', L"Vector Projection" ,-16384 ,16384}, + { L'F',L' ',L' ',L' ', L"Function Number" ,0 ,255}, + { L'I',L' ',L' ',L' ', L"Index into control value table" ,0 ,0}, + { L'J',L' ',L' ',L' ', L"Index into storage area" ,0 ,0}, + { L'*',L' ',L' ',L' ', L"Anything" ,SHORTMIN ,SHORTMAX}, + { L'&',L' ',L' ',L' ', L"Entire Stack" ,SHORTMIN ,SHORTMAX}, + { L'C',L' ',L' ',L' ', L"Contour Number" ,0 ,0}, + { L'N', L' ',L' ',L' ', L"Small Number" ,0 ,255}, + { L'V',L' ',L' ',L' ', L"Positive Value" ,0 ,SHORTMAX}, /* New values added to use RangeChecking*/ + { L'L', L' ',L' ',L' ', L"Label" ,SHORTMIN ,SHORTMAX}, /* new ClaudeBe, to parse the label of a JR instruction */ + { L'A', L' ',L' ',L' ', L"Unsigned Byte" ,0 ,255}, /* NPUSH, number of puses */ + { L'H', L' ',L' ',L' ', L"Signed Word" ,SHORTMIN ,SHORTMAX} /* PUSHW argument */ +}; /* See typedef asm_PushAndPopDescriptionType (above) 6-7-90 JTS Range Checking Adds */ + +#define NumberOfPPEntries 15 + +tt_CompilerSwitchType tt_CompilerSwitch[] = { +/**** Instruction name, descriptive info, index *****/ + { L"PUSH", L"Push arguments on the stack", tt_Push_Switch }, + { L"PUSHON", L"Set direct push mode on", tt_PushOn_Switch }, + { L"PUSHOFF", L"Set direct push mode off", tt_PushOff_Switch }, + { L"BEGIN", L"Beginning of a block", tt_Begin_Switch }, + { L"END", L"End of a block", tt_End_Switch }, + { L"GHTBLOCK", L"Beginning of a new Sampo block", tt_GHTBlockBegin_Switch }, + { L"GHTB", L"Beginning of a new Sampo block", tt_GHTBlockBegin_Switch }, + { L"GHTE", L"End of a new Sampo block", tt_GHTBlockEnd_Switch } +}; + +#define asm_SLOOP 0x17 + +typedef struct { + const wchar_t *name; /* Apple instruction name */ + const wchar_t *description; /* descriptive info */ + const wchar_t *pops; /* What the instruction pops */ + const wchar_t *pushes; /* What the instruction pushes */ + unsigned short baseCode; /* base code, need to be short because of the fake code and the duplicate DELTA */ + const wchar_t *booleans; /* booleans */ +} tt_InstructionType; + +/****** + array defining the TrueType instructions, it look like : +tt_InstructionType tt_instruction[] = { +/+ Instruction name, descriptive info, pops, pushes, base code, booleans -/ + { "SVTCA", L"Set Vectors To Coordinate Axis", L"", L"", 0x00, "A" }, +....... + +*****/ + +#define tt_TOTALNUMBEROFINSTRUCTIONS 178 + +const tt_InstructionType tt_instruction[] = { +/**** Instruction name, descriptive info, pops, pushes, base code, booleans *****/ + { L"SVTCA", L"Set Vectors To Coordinate Axis", L"", L"", 0x00, L"A"}, + { L"SPVTCA", L"Set Projection Vector To Coordinate Axis", L"", L"", 0x02, L"A"}, + { L"SFVTCA", L"Set Freedom Vector To To Cordinate Axis", L"", L"", 0x04, L"A"}, + { L"SPVTL", L"Set Projection Vector To Line", L"PP", L"", 0x06, L"R"}, + { L"SFVTL", L"Set Freedom Vector To Line", L"PP", L"", 0x08, L"R"}, + { L"SPVFS", L"Set Projection Vector From Stack", L"RR", L"", 0x0A, L"" }, + { L"SFVFS", L"Set Freedom Vector From Stack", L"RR", L"", 0x0B, L"" }, + { L"GPV", L"Get Projection Vector", L"", L"RR", 0x0C, L"" }, + { L"GFV", L"Get Freedom Vector", L"", L"RR", 0x0D, L"" }, + { L"SFVTPV", L"Set Freedom Vector To Projection Vector", L"", L"", 0x0E, L"" }, + { L"ISECT", L"InterSECT", L"PPPPP", L"", 0x0F, L"" }, + { L"SRP0", L"Set Reference Point 0", L"P", L"", 0x10, L"" }, + { L"SRP1", L"Set Reference Point 1", L"P", L"", 0x11, L"" }, + { L"SRP2", L"Set Reference Point 2", L"P", L"", 0x12, L"" }, + { L"SZP0", L"Set Zone Pointer 0", L"E", L"", 0x13, L"" }, /* New Name */ + { L"SGEP0", L"Set Glyph Element Pointer 0", L"E", L"", 0x13, L"" }, /* Old Name */ + { L"SZP1", L"Set Zone Pointer 1", L"E", L"", 0x14, L"" }, /* New Name */ + { L"SGEP1", L"Set Glyph Element Pointer 1", L"E", L"", 0x14, L"" }, /* Old Name */ + { L"SZP2", L"Set Zone Pointer 2", L"E", L"", 0x15, L"" }, /* New Name */ + { L"SGEP2", L"Set Glyph Element Pointer 2", L"E", L"", 0x15, L"" }, /* Old Name */ + { L"SZPS", L"Set Zone PointerS", L"E", L"", 0x16, L"" }, /* New Name */ + { L"SGEPS", L"Set Glyph Element PointerS", L"E", L"", 0x16, L"" }, /* Old Name */ + { L"SLOOP", L"Set Loop Variable", L"V", L"", asm_SLOOP, L"" }, + { L"RTG", L"Round To Grid", L"", L"", 0x18, L"" }, + { L"RTHG", L"Round To Half Grid", L"", L"", 0x19, L"" }, + { L"SMD", L"Set Minimum Distance", L"D", L"", 0x1A, L"" }, + { L"ELSE", L"Else", L"", L"", 0x1B, L"" }, + { L"JR", L"Jump Relative", L"L", L"", 0x1C, L"" }, + { L"JMPR", L"Jump Relative", L"L", L"", 0x1C, L"" }, /* name documented in TrueType doc */ + { L"SCVTCI", L"Set Control Value Table Cut In", L"D", L"", 0x1D, L"" }, + { L"SSWCI", L"Set Single Width Cut In", L"D", L"", 0x1E, L"" }, + { L"SSW", L"Set Single Width", L"D", L"", 0x1F, L"" }, + { L"DUP", L"Duplicate", L"*", L"**", 0x20, L"" }, + { L"POP", L"POP top element off the stack", L"*", L"", 0x21, L"" }, + { L"CLEAR", L"CLEAR entire stack", L"&", L"", 0x22, L"" }, + { L"SWAP", L"SWAP two top elements", L"**", L"**", 0x23, L"" }, + { L"DEPTH", L"DEPTH of stack", L"", L"V", 0x24, L"" }, + { L"CINDEX", L"Copy INDEXed element to the top of the stack", L"V", L"*", 0x25, L"" }, + { L"MINDEX", L"Move INDEXed element to the top of the stack", L"V", L"", 0x26, L"" }, + { L"ALIGNPTS", L"ALIGN PoinTS", L"PP", L"", 0x27, L"" }, + { L"RAW", L"Read Advance Width", L"", L"D", 0x28, L"" }, + { L"UTP", L"UnTouch Point", L"P", L"", 0x29, L"" }, + { L"LOOPCALL", L"LOOP while CALLing function", L"VF", L"", 0x2A, L"" }, + { L"CALL", L"CALL function", L"F", L"", 0x2B, L"" }, + { L"FDEF", L"Function DEFinition", L"F", L"", 0x2C, L"" }, + { L"ENDF", L"END Function definition", L"", L"", 0x2D, L"" }, + { L"MDAP", L"Move Direct Absolute Point", L"P", L"", 0x2E, L"R" }, + { L"IUP", L"Interpolate Untouched Points", L"", L"", 0x30, L"A" }, + { L"SHP", L"SHift Point", L"p", L"", 0x32, L"1" }, + { L"SHC", L"SHift Contour", L"C", L"", 0x34, L"1" }, + { L"SHZ", L"SHift Zone", L"E", L"", 0x36, L"1"}, /* NEW NAME */ + { L"SHE", L"SHift Element", L"E", L"", 0x36, L"1"}, /* OLD NAME */ + { L"SHPIX", L"SHift by fractional PIXel amount", L"pD", L"", 0x38, L"" }, + { L"IP", L"Interpolate Point", L"p", L"", 0x39, L"" }, + { L"MSIRP", L"Move Stack Indirect Relative Point", L"PD", L"", 0x3A, L"M" }, + { L"ALIGNRP", L"ALIGN Relative Point", L"p", L"", 0x3C, L"" }, + { L"RTDG", L"Round To Double Grid", L"", L"", 0x3D, L"" }, + { L"MIAP", L"Move Indirect Absolute Point", L"PI", L"", 0x3E, L"R" }, + { L"NPUSHB", L"PUSH n Bytes", L"NB", L"", 0x40, L"" }, + { L"NPUSHW", L"PUSH n Words", L"NW", L"", 0x41, L"" }, + { L"WS", L"Write Store", L"J*", L"", 0x42, L"" }, + { L"RS", L"Read Store", L"J", L"D", 0x43, L"" }, + { L"WCVTP", L"Write Control Value Table in Pixel units", L"ID", L"", 0x44, L"" }, + { L"RCVT", L"Read Control Value Table", L"I", L"D", 0x45, L"" }, + { L"GC", L"Get Coordinate value", L"P", L"D", 0x46, L"O" }, + { L"SCFS", L"Set Coordinate value From Stack", L"PD", L"", 0x48, L"" }, + { L"MD", L"Measure Distance", L"PP", L"D", 0x49, L"O"}, + { L"MPPEM", L"Measure Pixels Per EM", L"", L"V", 0x4B, L"" }, + { L"MPS", L"Measure Pointsize", L"", L"V", 0x4C, L"" }, + { L"FLIPON", L"set autoFLIP boolean ON", L"", L"", 0x4D, L"" }, + { L"FLIPOFF", L"set autoFLIP boolean OFF", L"", L"", 0x4E, L"" }, + { L"DEBUG", L"DEBUGger call", L"*", L"", 0x4F, L"" }, + { L"LT", L"Less Than", L"**", L"B", 0x50, L"" }, + { L"LTEQ", L"Less Than or EQual", L"**", L"B", 0x51, L"" }, + { L"GT", L"Greater Than", L"**", L"B", 0x52, L"" }, + { L"GTEQ", L"Greater Than or EQual", L"**", L"B", 0x53, L"" }, + { L"EQ", L"EQual", L"**", L"B", 0x54, L"" }, + { L"NEQ", L"Not EQual", L"**", L"B", 0x55, L"" }, + { L"ODD", L"ODD", L"*", L"B", 0x56, L"" }, + { L"EVEN", L"EVEN", L"*", L"B", 0x57, L"" }, + { L"IF", L"IF", L"B", L"", 0x58, L"" }, + { L"EIF", L"End IF", L"", L"", 0x59, L"" }, + { L"AND", L"AND", L"BB", L"B", 0x5A, L"" }, + { L"OR", L"OR", L"BB", L"B", 0x5B, L"" }, + { L"NOT", L"NOT", L"B", L"B", 0x5C, L"" }, + { L"DELTAP1", L"DELTA Point 1", L"***", L"", 0x5D, L"" }, + { L"SDB", L"Set Delta Base", L"*", L"", 0x5E, L"" }, + { L"SDS", L"Set Delta Shift", L"*", L"", 0x5F, L"" }, + { L"ADD", L"ADD", L"**", L"*", 0x60, L"" }, + { L"SUB", L"SUBTRACT", L"**", L"*", 0x61, L"" }, + { L"DIV", L"DIVide", L"**", L"*", 0x62, L"" }, + { L"MUL", L"MULtiply", L"**", L"*", 0x63, L"" }, + { L"ABS", L"ABSolute value", L"*", L"*", 0x64, L"" }, + { L"NEG", L"NEGate", L"*", L"*", 0x65, L"" }, + { L"FLOOR", L"FLOOR", L"*", L"*", 0x66, L"" }, + { L"CEILING", L"CEILING", L"*", L"*", 0x67, L"" }, + { L"ROUND", L"ROUND", L"*", L"*", 0x68, L"Cc" }, + { L"NROUND", L"No ROUND", L"*", L"*", 0x6C, L"Cc" }, + { L"WCVTF", L"Write Control Value Table in Funits", L"I*", L"", 0x70, L"" }, + { L"DELTAP2", L"DELTA Point 2", L"***", L"", 0x71, L"" }, + { L"DELTAP3", L"DELTA Point 3", L"***", L"", 0x72, L"" }, + { L"DELTAC1", L"DELTA Cvt 1", L"***", L"", 0x73, L"" }, + { L"DELTAC2", L"DELTA Cvt 2", L"***", L"", 0x74, L"" }, + { L"DELTAC3", L"DELTA Cvt 3", L"***", L"", 0x75, L"" }, + { L"SROUND", L"Super Round", L"*", L"", 0x76, L"" }, + { L"S45ROUND", L"Super 45 Round", L"*", L"", 0x77, L"" }, + { L"JROT", L"Jump Relative On True", L"LB", L"", 0x78, L"" }, + { L"JROF", L"Jump Relative On False", L"LB", L"", 0x79, L"" }, + { L"ROFF", L"Rounding Off", L"", L"", 0x7A, L"" }, + { L"RUTG", L"Round Up To Grid", L"", L"", 0x7C, L"" }, + { L"RDTG", L"Round Down To Grid", L"", L"", 0x7D, L"" }, + { L"SANGW", L"Set Angle Weight", L"*", L"", 0x7E, L"" }, + { L"AA", L"Adjust Angle", L"P", L"", 0x7F, L"" }, + { L"FLIPPT", L"Flip Point", L"p", L"", 0x80, L"" }, + { L"FLIPRGON", L"Flip Range On", L"PP", L"", 0x81, L"" }, + { L"FLIPRGOFF", L"Flip Range Off", L"PP", L"", 0x82, L"" }, + { L"USER83", L"User Defined 83", L"", L"", 0x83, L"" }, + { L"USER84", L"User Defined 84", L"", L"", 0x84, L"" }, + { L"SCANCTRL", L"Scan Converter Control", L"*", L"", 0x85, L"" }, + { L"SDPVTL", L"Set Dual Projection Vector To Line", L"PP", L"", 0x86, L"R" }, + { L"GETINFO", L"GET miscellaneous INFO", L"*", L"*", 0x88, L"" }, + { L"IDEF", L"Instruction DEFinition", L"*", L"", 0x89, L"" }, + { L"ROLL", L"ROLL 3 top stack elements", L"***", L"***", 0x8a, L"" }, /* NEW NAME */ + { L"ROT", L"ROTate 3 top stack elements", L"***", L"***", 0x8a, L"" }, /* OLD NAME */ + { L"MAX", L"MAXimum", L"**", L"*", 0x8b, L"" }, + { L"MIN", L"MINimum", L"**", L"*", 0x8c, L"" }, + { L"SCANTYPE", L"Scan Type", L"*", L"", 0x8d, L"" }, + { L"INSTCTRL", L"Instruction Control", L"*", L"", 0x8e, L"" }, + { L"USER8F", L"User Defined 8F", L"", L"", 0x8F, L"" }, + { L"USER90", L"User Defined 90", L"", L"", 0x90, L"" }, + { L"USER91", L"User Defined 91", L"", L"", 0x91, L"" }, + { L"GETVARIATION", L"Get Variation", L"", L"V", 0x91, L"" }, + { L"USER92", L"User Defined 92", L"", L"", 0x92, L"" }, + { L"USER93", L"User Defined 93", L"", L"", 0x93, L"" }, + { L"USER94", L"User Defined 94", L"", L"", 0x94, L"" }, + { L"USER95", L"User Defined 95", L"", L"", 0x95, L"" }, + { L"USER96", L"User Defined 96", L"", L"", 0x96, L"" }, + { L"USER97", L"User Defined 97", L"", L"", 0x97, L"" }, + { L"USER98", L"User Defined 98", L"", L"", 0x98, L"" }, + { L"USER99", L"User Defined 99", L"", L"", 0x99, L"" }, + { L"USER9A", L"User Defined 9A", L"", L"", 0x9A, L"" }, + { L"USER9B", L"User Defined 9B", L"", L"", 0x9B, L"" }, + { L"USER9C", L"User Defined 9C", L"", L"", 0x9C, L"" }, + { L"USER9D", L"User Defined 9D", L"", L"", 0x9D, L"" }, + { L"USER9E", L"User Defined 9E", L"", L"", 0x9E, L"" }, + { L"USER9F", L"User Defined 9F", L"", L"", 0x9F, L"" }, + { L"USERA0", L"User Defined A0", L"", L"", 0xA0, L"" }, + { L"USERA1", L"User Defined A1", L"", L"", 0xA1, L"" }, + { L"USERA2", L"User Defined A2", L"", L"", 0xA2, L"" }, + { L"USERA3", L"User Defined A3", L"", L"", 0xA3, L"" }, + { L"USERA4", L"User Defined A4", L"", L"", 0xA4, L"" }, + { L"USERA5", L"User Defined A5", L"", L"", 0xA5, L"" }, + { L"USERA6", L"User Defined A6", L"", L"", 0xA6, L"" }, + { L"USERA7", L"User Defined A7", L"", L"", 0xA7, L"" }, + { L"USERA8", L"User Defined A8", L"", L"", 0xA8, L"" }, + { L"USERA9", L"User Defined A9", L"", L"", 0xA9, L"" }, + { L"USERAA", L"User Defined AA", L"", L"", 0xAA, L"" }, + { L"USERAB", L"User Defined AB", L"", L"", 0xAB, L"" }, + { L"USERAC", L"User Defined AC", L"", L"", 0xAC, L"" }, + { L"USERAD", L"User Defined AD", L"", L"", 0xAD, L"" }, + { L"USERAE", L"User Defined AE", L"", L"", 0xAE, L"" }, + { L"USERAF", L"User Defined AF", L"", L"", 0xAF, L"" }, + { L"PUSHB", L"PUSH Bytes", L"", L"", 0xB0, L"P" }, /* fix this */ + { L"PUSHW", L"PUSH Words", L"", L"", 0xB8, L"P" }, + { L"MDRP", L"Move Direct Relative Point", L"P", L"", 0xC0, L"M>RCc" }, + { L"MIRP", L"Move Indirect Relative Point", L"PI", L"", 0xE0, L"M>RCc" }, + + { L"DLTP1", L"DELTA Point 1, direct", L"***", L"", 0x15D, L"" }, + { L"DLTP2", L"DELTA Point 2, direct", L"***", L"", 0x171, L"" }, + { L"DLTP3", L"DELTA Point 3, direct", L"***", L"", 0x172, L"" }, + { L"DLTC1", L"DELTA Cvt 1, direct", L"***", L"", 0x173, L"" }, + { L"DLTC2", L"DELTA Cvt 2, direct", L"***", L"", 0x174, L"" }, + { L"DLTC3", L"DELTA Cvt 3, direct", L"***", L"", 0x175, L"" }, + /* Jie 6-22-90 */ + /* #define FakeCode 0xAF*/ + #define FakeCode 0xFFFF + { L"OFFSET", L"Component character", L"V**", L"", FakeCode, L"R" }, + { L"SOFFSET", L"Component character", L"V******", L"", FakeCode, L"R" }, + { L"ANCHOR", L"Component character", L"V**", L"", FakeCode, L"" }, + { L"SANCHOR", L"Component character", L"V******", L"", FakeCode, L"" }, + { L"OVERLAP", L"Component character", L"", L"", FakeCode, L"" }, + { L"NONOVERLAP",L"Component character", L"", L"", FakeCode, L"" }, + { L"USEMYMETRICS",L"Component character", L"", L"", FakeCode, L"" }, + { L"SCALEDCOMPONENTOFFSET",L"Component character", L"", L"", FakeCode, L"" }, + { L"UNSCALEDCOMPONENTOFFSET",L"Component character", L"", L"", FakeCode, L"" }, +}; + +/* if we add/remove instructions, we should update tt_TOTALNUMBEROFINSTRUCTIONS in the .h file too */ +#define TOTALNUMBEROFINSTRUCTIONS (sizeof(tt_instruction) / sizeof(tt_InstructionType)) + +const asm_BooleanTranslationType asm_booleanTranslation1[] = { + { L'A', L'X', 1 }, + { L'A', L'Y', 0 }, + { L'O', L'O', 1 }, + { L'O', L'N', 0 }, + { L'R', L'R', 1 }, + { L'R', L'r', 0 }, + { L'M', L'M', 1 }, + { L'M', L'm', 0 }, + { L'1', L'1', 1 }, + { L'1', L'2', 0 }, + { L'>', L'>', 1 }, + { L'>', L'<', 0 }, + { L'C', L'G', 0 }, + { L'C', L'B', 0 }, + { L'C', L'W', 0 }, + { L'c', L'r', 0 }, + { L'c', L'l', 1 }, + { L'c', L'h', 2 }, + { L'P', L'1', 0 }, + { L'P', L'2', 1 }, + { L'P', L'3', 2 }, + { L'P', L'4', 3 }, + { L'P', L'5', 4 }, + { L'P', L'6', 5 }, + { L'P', L'7', 6 }, + { L'P', L'8', 7 } +}; + +#define NumberOfBooleanTranslations (sizeof(asm_booleanTranslation1) / sizeof(asm_BooleanTranslationType)) + +#define TOTALNUMBEROFSWITCH (sizeof(tt_CompilerSwitch) / sizeof(tt_CompilerSwitchType)) + + + +/**** label related code *****/ +/**** moved and adapted from label.c */ + +#define MAXJRPAIR 200 /* max. number of labels "#L100" or JR[] + * lines in one block (#BEGIN-#END + */ +#define MAXLABELLENGTH 22 + +typedef struct { + wchar_t label[MAXLABELLENGTH]; /* label ["#L100"] */ + short iPos; /* instruction position from #BEGIN */ + short *aPtr; /* start ptr of argument storage */ + + short cArg; + wchar_t *linePtr; /* pointer in the source to be able to display the location of an error */ +}tt_jrWordType; + + +/* + * labels + * e.g + * #L100: + */ +typedef struct { + short num; + tt_jrWordType *lab [MAXJRPAIR]; +}tt_LabelType; + +/* + * When #PUSHON + * JR[], #L100 + * or JROF[], #L100, 1 + */ + +typedef struct { + short num; + tt_jrWordType *jr [MAXJRPAIR]; +}tt_JRtype; + +/* + * One #PUSH statement + * eg. #PUSH, 1,2, B1, W2, 3,4 + */ + +#define PUSH_ARG 256 /* max. num of argument on one PUSH [6] */ + +typedef struct { + wchar_t label[MAXLABELLENGTH]; /* label ["#L100"] */ + short LocalIndex; /* argument index from the beginning of the #PUSH */ + + short IsAByte; + unsigned char *aPtr; /* ptr of argument storage */ + wchar_t *linePtr; /* pointer in the source to be able to display the location of an error */ +}tt_psType; + +typedef struct { + short num; /* number of total PUSH statements in a block */ + tt_psType *ps [MAXJRPAIR]; +}tt_PStype; + +/* + * When #PUSHOFF + * e.g. + * JR[], ( B1 = #L100 ) + */ + +typedef struct { + wchar_t label[MAXLABELLENGTH]; /* Label ["#L100"] */ + wchar_t BWLabel[MAXLABELLENGTH]; /* BW word ["B1"} */ + short iPos; /* instruction position from #BEGIN */ + wchar_t *linePtr; /* pointer in the source to be able to display the location of an error */ +}tt_JrBWwordType; + + +typedef struct { + short num; + tt_JrBWwordType *bw [MAXJRPAIR]; +}tt_JrBWtype; + + +int32_t TT_GetLineLength( wchar_t *p, wchar_t *endP); +int32_t TT_GetLineLength( wchar_t *p, wchar_t *endP) +{ + int32_t LineLength; + + LineLength = 0; + while ( !(*p == L'\x0D') && !(*p == L'\x0A') && (p < endP)) // Allow both '\r' and '\n' to terminate lines. + { + LineLength++; p++; + } + return LineLength; +} + +int32_t TT_GetStringLength( wchar_t *p, wchar_t *endP); +int32_t TT_GetStringLength( wchar_t *p, wchar_t *endP) +{ + int32_t StringLength; + + StringLength = 0; + while ( ( (*p >= L'A' && *p <= L'Z') || (*p >= L'a' && *p <= L'z') || (*p >= L'0' && *p <= L'9') ) && (p < endP)) + { + StringLength++; p++; + } + return StringLength; +} + +void TT_SavePushLabel(wchar_t * CurrentPtr, short numberofLocalArgs,int32_t stringLenth,wchar_t *p,tt_PStype *PS, short * tt_error); +void TT_SavePushLabel(wchar_t * CurrentPtr, short numberofLocalArgs,int32_t stringLenth,wchar_t *p,tt_PStype *PS, short * tt_error) +{ + short i, k; + + for ( k = PS->num-1; k >=0; k--) + { + if (wcsncmp( PS->ps[k]->label, p, stringLenth) == 0 && (int32_t)STRLENW(PS->ps[k]->label) == stringLenth ) + { + *tt_error = tt_DuplicateLabel; + return; + } + } + + + k = PS->num; + PS->ps[k] = ( tt_psType *) NewP (sizeof (tt_psType ) ); + if (PS->ps[k] == NULL) { + *tt_error = tt_NotEnoughMemory; + return; + } + + PS->ps[k]->aPtr = NULL; /* we don't know yet, depend on the optimisation of the PUSH */ + PS->ps[k]->linePtr = CurrentPtr; + + PS->ps[k]->LocalIndex = numberofLocalArgs; /* argument index from the beginning of the #PUSH */ + + PS->ps[k]->IsAByte = false; + if (p[0] == L'B') PS->ps[k]->IsAByte = true; + + for ( i = 0; i < stringLenth; i++) { /* copy label */ + PS->ps[k]->label[i] = p[i]; + } + PS->ps[k]->label[stringLenth] = '\0'; + + PS->num ++; + if ( PS->num >= MAXJRPAIR ) { + *tt_error = tt_TooManyLabels; + } +} + + +wchar_t *TT_ParseNumber( wchar_t *p, wchar_t *endP,short *Number, int32_t * SelectionLength, short * error ); +wchar_t *TT_ParseNumber( wchar_t *p, wchar_t *endP,short *Number, int32_t * SelectionLength, short * error ) +{ + short i, negatif; + int32_t tempNumber,maxNum; + wchar_t *pNumStart; + + /* skip white space */ + while ( *p == L' ' && p < endP) + p++; + + tempNumber = 0; + negatif = false; + i = 0; + + if ( *p == L'-' && p < endP ) + { + negatif = true; + p++; + } + + if ( *p == L'+' && p < endP && !negatif) + /* we accept + or - but don't want -+ together */ + { + p++; + } + + maxNum = negatif ? SHORTMAX : USHORTMAX; // allow full unicode range for glyph index B.St. + + pNumStart = p; + + if ( *p == L'0' && *(p+1) == L'x' && (p+2 < endP)) + { + /* there is an hexadecimal number */ + p++; p++; + while ( ((*p >= L'0' && *p <= L'9') || (*p >= L'A' && *p <= L'F') || (*p >= L'a' && *p <= L'f') ) && p < endP ) + { + + if ( *p >= L'0' && *p <= L'9' ) + { + tempNumber = tempNumber * 16 + (int32_t) *p - (int32_t) L'0'; + } else if ( *p >= L'A' && *p <= L'F') + { + tempNumber = tempNumber * 16 + (int32_t) *p - (int32_t) L'A' + 10; + } else if ( *p >= L'a' && *p <= L'f') + { + tempNumber = tempNumber * 16 + (int32_t) *p - (int32_t) L'a' + 10; + } + if (tempNumber > maxNum) + { + *error = tt_ParseOverflow; + *SelectionLength = (int32_t)((ptrdiff_t)(p - pNumStart) + 1); + return pNumStart; + } + p++; + i++; + } + } else { + while ( *p >= L'0' && *p <= L'9' && p < endP ) + { + tempNumber = tempNumber * 10 + (short) *p - (short) L'0'; + if (tempNumber > maxNum) + { + *error = tt_ParseOverflow; + *SelectionLength = (int32_t)((ptrdiff_t)(p - pNumStart) + 1); + return pNumStart; + } + p++; + i++; + } + } + if (i == 0) *error = tt_UnableToParseArgument; + + *Number = (short)tempNumber; + if (negatif) *Number = - (*Number); + return p; + +} + +/*--------------------- Save to database ----------------*/ + +/* + * Parse *p = "#PUSH, 1,2, B1, W1, 3,4 \x0D" + * + * argStore : start ptr of argument storage + * *argIdex : return total number of arguments [6] + * if label begins with 'B', a byte is reserved + * if label begins with 'W', an integer is reserved + * + * all the information is put into *ps + * if lableFlag = false; memory *ps is freed + */ +wchar_t *TT_ParsePUSHandSave(tt_PStype *ps,wchar_t *CurrentPtr,wchar_t * EOLPtr,short *argStore,short *argIdex, int32_t * SelectionLength, short * tt_error ); +wchar_t *TT_ParsePUSHandSave(tt_PStype *ps,wchar_t *CurrentPtr,wchar_t * EOLPtr,short *argStore,short *argIdex, int32_t * SelectionLength, short * tt_error ) +{ + + + + (*argIdex) = 0; + + while (CurrentPtr <= EOLPtr) + { + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if (CurrentPtr >= EOLPtr) break; + + /* look for the comma */ + if ( *CurrentPtr != L',' ) + { + break; /* it could be a comment */ + } + CurrentPtr = CurrentPtr +1; + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if (CurrentPtr >= EOLPtr) break; + + if ( *CurrentPtr == L'B' || *CurrentPtr == L'W' ) + { + int32_t StringLength; + if ( *CurrentPtr == L'B') argStore[(*argIdex)] = 55; /* reserve the space for the jump, one byte or one word */ + else if (*CurrentPtr == L'W' ) argStore[(*argIdex)] = 5555; + + StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); + if (StringLength < 2) + { + *tt_error = tt_VoidLabel; + return (CurrentPtr); + } + + if (StringLength >= MAXLABELLENGTH) + { + *tt_error = tt_LabelTooLong; + return (CurrentPtr); + } + + TT_SavePushLabel(CurrentPtr, *argIdex,StringLength,CurrentPtr,ps, tt_error); + + CurrentPtr = CurrentPtr + StringLength; + } else { + /* there should be a number */ + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &argStore[(*argIdex)], SelectionLength, tt_error); + if (*tt_error != tt_NoError) return CurrentPtr; + } + (*argIdex) ++; + if (*argIdex == PUSH_ARG) + { + *tt_error = tt_TooManyPushArgs; + return (CurrentPtr-1); + } + } + return CurrentPtr; + + +} + + +/* + * given label = "#L100" + * return the index in the Label->lab[index] + * return -1 on error + */ +static short TT_findLabelPos(tt_LabelType *Label,wchar_t *label, short * tt_error ); +static short TT_findLabelPos(tt_LabelType *Label,wchar_t *label, short * tt_error ) +{ + short i; + + for ( i = 0; i < Label->num; i++) { + if ( ! wcscmp ( label, Label->lab[i]->label) ) { + return Label->lab[i]->iPos; + } + } + *tt_error = tt_LabelNotFound; + return -1; +} + +/* + * repostion for + * JR[], #L100 or JROF[], #L100, 1 + * when #PUSHON + */ +void TT_JRpushON_ReplaceLabel(tt_JRtype *JR,tt_LabelType *Label,short *argStore, short * tt_error); +void TT_JRpushON_ReplaceLabel(tt_JRtype *JR,tt_LabelType *Label,short *argStore, short * tt_error) +{ + short i, JRiPos, labeliPos, delta, index; + + for ( i = 0; i < JR->num; i++) { + labeliPos = TT_findLabelPos( Label, JR->jr[i]->label, tt_error ); + JRiPos = JR->jr[i]->iPos; + + delta = labeliPos - JRiPos; + /* if ( delta < 0 ) delta --; */ + + index = JR->jr[i]->cArg; + /* JR->jr[i]->aPtr[index] = delta; */ + argStore[index] = delta; + /* + printf(L"\n JRlabel=" WIDE_STR_FORMAT L" JRipos = %hd, labeliPos= %hd, delta = %hd", + JR->jr[i]->label, JRiPos, labeliPos, delta ); + */ + } +} + + +/* + * repostion the JR[], ( B1 = #L100) type arguments + */ + +void TT_JRpushOFF_ReplaceLabel(tt_JrBWtype *JrBW,tt_PStype *PS,tt_LabelType *Label, short * tt_error); +void TT_JRpushOFF_ReplaceLabel(tt_JrBWtype *JrBW,tt_PStype *PS,tt_LabelType *Label, short * tt_error) +{ + short i, labeliPos, delta, index; + + for ( i = 0; i < PS->num; i++) { + /* for each #PUSH Bn or Wn, look fot the corresponding JR */ + for ( index = 0; index < JrBW->num; index++) { + if ( ! wcscmp( PS->ps[i]->label,JrBW->bw[index]->BWLabel) ) break; + } + + labeliPos = TT_findLabelPos( Label, JrBW->bw[index]->label, tt_error ); + delta = labeliPos - JrBW->bw[i]->iPos; + + if ( (*PS->ps[i]).IsAByte && (delta > 255 )) + { + *tt_error = tt_JumpTooBigForByte; + } + if ( (*PS->ps[i]).IsAByte && ( delta < 0 )) + { + *tt_error = tt_JumpNegativeForByte; + } + + if ( (*PS->ps[i]).IsAByte ) + { + *(*PS->ps[i]).aPtr = (unsigned char) delta; + } else { + short * sTempPtr; + sTempPtr = (short *) (*PS->ps[i]).aPtr; + *sTempPtr = SWAPW(delta); // B.St. + } + + } +} + + +wchar_t * TT_FindLabelError(tt_PStype *PS, tt_JrBWtype *JrBW, tt_JRtype *JR, tt_LabelType *Label, wchar_t * CurrentPtr, int32_t * SelectionLength, short * tt_error) ; +wchar_t * TT_FindLabelError(tt_PStype *PS, tt_JrBWtype *JrBW, tt_JRtype *JR, tt_LabelType *Label, wchar_t * CurrentPtr, int32_t * SelectionLength, short * tt_error) +{ + short i, k; + + /* look through the push on JR */ + if ( JR->num != 0) + { + for ( k = 0; k < JR->num; k++) { + for ( i = 0; i < Label->num; i++) { + if ( ! wcscmp( JR->jr[k]->label,Label->lab[i]->label) ) break; + } + if ( i >= Label->num ) { + *tt_error = tt_LabelNotFound; + *SelectionLength = (int32_t)STRLENW(JR->jr[k]->label); + return JR->jr[k]->linePtr; + } + } + } + + /* look through the push off JR */ + if ( JrBW->num != 0) + { + for ( k = 0; k < JrBW->num; k++) { + for ( i = 0; i < Label->num; i++) { + if ( ! wcscmp( JrBW->bw[k]->label,Label->lab[i]->label) ) break; + } + if ( i >= Label->num ) { + *tt_error = tt_LabelNotFound; + *SelectionLength = (int32_t)STRLENW(JrBW->bw[k]->label); + return JrBW->bw[k]->linePtr; + } + + /* look through the #PUSH,Bn,Wn for the corresponding label */ + for ( i = 0; i < PS->num; i++) { + if ( ! wcscmp( JrBW->bw[k]->BWLabel,PS->ps[i]->label) ) break; + } + if ( i >= PS->num ) { + *tt_error = tt_LabelNotFound; + *SelectionLength = (int32_t)STRLENW(JrBW->bw[k]->BWLabel); + return JrBW->bw[k]->linePtr; + } + } + } + /* check if every #PUSH,Bn,Wn has a corresponding JR[], */ + if ( PS->num != 0) + { + for ( k = 0; k < PS->num; k++) { + for ( i = 0; i < JrBW->num; i++) { + if ( ! wcscmp( PS->ps[k]->label,JrBW->bw[i]->BWLabel) ) break; + } + if ( i >= JrBW->num ) { + *tt_error = tt_LabelNotFound; + *SelectionLength = (int32_t)STRLENW(PS->ps[k]->label); + return PS->ps[k]->linePtr; + } + } + } + return CurrentPtr; +} + +/* + * Save Label information into struct label_LabelType *Label. + * input: + * char *p = #L100:" + * numberofArgs : number of arguments so far (from #BEGIN). + * numberofInstructions : number of instructions so far (from #BEGIN). + * stringLenth : length of "#L100:" + * + * all the information is put into Label + */ + + +void TT_SaveLabel(short numberofArgs,short numberofInstructions,int32_t stringLenth,wchar_t *p,tt_LabelType *Label, short * tt_error); +void TT_SaveLabel(short numberofArgs,short numberofInstructions,int32_t stringLenth,wchar_t *p,tt_LabelType *Label, short * tt_error) +{ + short i, k; + + for ( k = Label->num-1; k >=0; k--) + { + if (wcsncmp( Label->lab[k]->label, p, stringLenth) == 0 && (int32_t)STRLENW(Label->lab[k]->label) == stringLenth ) + { + *tt_error = tt_DuplicateLabel; + return; + } + } + + + k = Label->num; + Label->lab[k] = ( tt_jrWordType *) NewP (sizeof (tt_jrWordType ) ); + if (Label->lab[k] == NULL) { + *tt_error = tt_NotEnoughMemory; + return; + } + + + Label->lab[k]->linePtr = p; + Label->lab[k]->iPos = numberofInstructions; + + for ( i = 0; i < stringLenth; i++) { /* copy label */ + Label->lab[k]->label[i] = p[i]; + } + Label->lab[k]->label[stringLenth] = '\0'; + + Label->num ++; + if ( Label->num >= MAXJRPAIR ) { + *tt_error = tt_TooManyLabels; + } +} + +//void TT_SavePSLabel(short numberofArgs,short numberofInstructions,short stringLenth,char *p,tt_LabelType *Label, short * tt_error); + +wchar_t * TT_SaveJR(short numberofArgs,short numberofInstructions,wchar_t * CurrentPtr, wchar_t *LabelPtr,int32_t stringLenth, + wchar_t *BWLabelPtr,short BWstringLenth,tt_JRtype *JRList,tt_JrBWtype *JrBW, short *aPtr,int32_t * SelectionLength, short * tt_error); +wchar_t * TT_SaveJR(short numberofArgs,short numberofInstructions,wchar_t * CurrentPtr, wchar_t *LabelPtr,int32_t stringLenth, + wchar_t *BWLabelPtr,short BWstringLenth,tt_JRtype *JRList,tt_JrBWtype *JrBW, short *aPtr,int32_t * SelectionLength, short * tt_error) +{ + short i, k; + + for ( k = JRList->num-1; k >=0; k--) + { + if (wcsncmp( JRList->jr[k]->label, LabelPtr, stringLenth) == 0 && (int32_t)STRLENW(JRList->jr[k]->label) == stringLenth ) + { + *tt_error = tt_DuplicateLabel; + *SelectionLength = stringLenth; + return LabelPtr; + } + } + + if (BWstringLenth != 0) + { + + for ( k = JrBW->num-1; k >=0; k--) + { + if (wcsncmp( JrBW->bw[k]->BWLabel, BWLabelPtr, BWstringLenth) == 0 && (int32_t)STRLENW(JrBW->bw[k]->BWLabel) == stringLenth ) + { + *tt_error = tt_DuplicateLabel; + *SelectionLength = BWstringLenth; + return LabelPtr; + } + } + + k = JrBW->num; + JrBW->bw[k] = ( tt_JrBWwordType *) NewP (sizeof (tt_JrBWwordType ) ); + if (JrBW->bw[k] == NULL) { + *tt_error = tt_NotEnoughMemory; + *SelectionLength = stringLenth; + return LabelPtr; + } + + JrBW->bw[k]->linePtr = LabelPtr; + JrBW->bw[k]->iPos = numberofInstructions; + + for ( i = 0; i < stringLenth; i++) { /* copy label */ + JrBW->bw[k]->label[i] = LabelPtr[i]; + } + JrBW->bw[k]->label[stringLenth] = '\0'; + + for ( i = 0; i < BWstringLenth; i++) { /* copy label */ + JrBW->bw[k]->BWLabel[i] = BWLabelPtr[i]; + } + JrBW->bw[k]->BWLabel[BWstringLenth] = '\0'; + + JrBW->num ++; + if ( JrBW->num >= MAXJRPAIR ) { + *tt_error = tt_TooManyLabels; + *SelectionLength = stringLenth; + return LabelPtr; + } + + } else { + + /* no Bn or Wn label */ + k = JRList->num; + JRList->jr[k] = ( tt_jrWordType *) NewP (sizeof (tt_jrWordType ) ); + if (JRList->jr[k] == NULL) { + *tt_error = tt_NotEnoughMemory; + *SelectionLength = stringLenth; + return LabelPtr; + } + + + JRList->jr[k]->aPtr = aPtr; + JRList->jr[k]->cArg = numberofArgs; + + + JRList->jr[k]->linePtr = LabelPtr; + JRList->jr[k]->iPos = numberofInstructions; + + for ( i = 0; i < stringLenth; i++) { /* copy label */ + JRList->jr[k]->label[i] = LabelPtr[i]; + } + JRList->jr[k]->label[stringLenth] = '\0'; + + JRList->num ++; + if ( JRList->num >= MAXJRPAIR ) { + *tt_error = tt_TooManyLabels; + *SelectionLength = stringLenth; + return LabelPtr; + } + } + return CurrentPtr; +} + + +void TT_FreeAllLabelMemory( tt_PStype *PS, tt_JrBWtype *JrBW, tt_LabelType *Label, tt_JRtype *JR); +void TT_FreeAllLabelMemory( tt_PStype *PS, tt_JrBWtype *JrBW, tt_LabelType *Label, tt_JRtype *JR) +{ + + short k; + + for ( k = Label->num-1; k >=0; k--) DisposeP((void**)&Label->lab[k]); + for ( k = JR->num-1; k >=0; k--) DisposeP((void**)&JR->jr[k] ); + for ( k = JrBW->num-1; k >=0; k--) DisposeP((void**)&JrBW->bw[k]); + for ( k = PS->num-1; k >=0; k--) DisposeP((void**)&PS->ps[k]); + + DisposeP((void**)&Label); + DisposeP((void**)&JR ); + DisposeP((void**)&JrBW); + DisposeP((void**)&PS); +} + +/**** end of label related code ****/ + +short TT_ExpandArgs( const wchar_t *src,wchar_t * dest, short *loop , short * tt_error); +short TT_ExpandArgs( const wchar_t *src,wchar_t * dest, short *loop, short * tt_error ) +// char *src, *dest; +// short *loop; +{ + wchar_t c, *base; + short i; + short usesLoop; + + usesLoop = false; + base = dest; + do { + i = *loop; + c = *src++; + if ( c >= L'a' && c <= L'z' ) { + usesLoop = true; + c -= L'a' - L'A'; /* turn into uppercase */ + while ( i > 1 && c && ( dest-base <= ARGTYPEBUFFER_SIZE )) { + i--; + *dest++ = c; + } + } + if ( dest-base > ARGTYPEBUFFER_SIZE ) { + *tt_error = tt_SLOOPArgumentBufferTooSmall; + return usesLoop; + } + + } while ( (*dest++ = c) != 0); + *loop = i; + + return usesLoop; +} + +/* function to be called before TT_Compile to set the maximum values for the range check */ +void TT_SetRangeCheck(short LastContNb, short LastPointNb, short LastZoneNb, short LastFunctionNb, short LastCvtNumber, short LastStorageIndex); +void TT_SetRangeCheck(short LastContNb, short LastPointNb, short LastElementNb, short LastFunctionNb, short LastCvtNumber, short LastStorageIndex) + +{ + /* Point Number */ + asm_ppDescription1[0].highestValidValue = LastPointNb; + + /* Element Number */ + if ( LastElementNb == 0 ) { + asm_ppDescription1[1].lowestValidValue = 1; + asm_ppDescription1[1].highestValidValue = 1; + } else { + asm_ppDescription1[1].lowestValidValue = 0; + asm_ppDescription1[1].highestValidValue = LastElementNb; + } + + /* function number: */ + asm_ppDescription1[5].highestValidValue = LastFunctionNb; + + + /*Index into control value table:*/ + asm_ppDescription1[6].highestValidValue = LastCvtNumber; + + /*Index into storage area*/ + asm_ppDescription1[7].highestValidValue = LastStorageIndex; + + /*Number of Contours*/ + asm_ppDescription1[10].highestValidValue = LastContNb; +} + + +wchar_t * TT_ReadInstructionBooleans (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned short *InstructionCode, int32_t * Selectionlength, short * tt_error); +wchar_t * TT_ReadInstructionBooleans (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned short *InstructionCode, int32_t * Selectionlength, short * tt_error) +{ +/* the instruction code may be changed according to the boolean flags */ + short booleanCount, booleanShift, NBOfBooleans, found, k; + wchar_t * tempP; + if ( *CurrentPtr != L'[' /* ] for balance */ || CurrentPtr >= EOLPtr) + { + *tt_error = tt_BooleanFlagsMissing; + *Selectionlength = 1; + return CurrentPtr; + }; + tempP = CurrentPtr; + CurrentPtr ++; + NBOfBooleans = (short)STRLENW(tt_instruction[InstructionIndex].booleans) ; + booleanShift = NBOfBooleans; + + for (booleanCount = 0; + // for balance '[' + booleanCount < NBOfBooleans && *CurrentPtr != L']' && CurrentPtr < EOLPtr ; + booleanCount++ ) + { + booleanShift--; + for ( found = k = 0; k < (short)NumberOfBooleanTranslations; k++ ) { + if ( asm_booleanTranslation1[k].type == tt_instruction[InstructionIndex].booleans[booleanCount] && + (asm_booleanTranslation1[k].code) == *CurrentPtr ) + { + found = 1; + *InstructionCode = *InstructionCode + (asm_booleanTranslation1[k].result << booleanShift); + } + } + if (!found) + { + *tt_error = tt_UnrecognizedBoolean; + *Selectionlength = 1; + return CurrentPtr; + } + + CurrentPtr++; + } + + if (booleanCount != NBOfBooleans) + { + *tt_error = tt_WrongNumberOfBoolean; + *Selectionlength = (short)(CurrentPtr-tempP)+1; + return tempP; + }; + + if ( (CurrentPtr >= EOLPtr) || (*CurrentPtr == L',' ) ) + { + *tt_error = tt_MissingClosingBracket; + *Selectionlength = (short)(CurrentPtr-tempP); + return tempP; + }; + + if ( /* [ for balance */ *CurrentPtr != L']' ) + { + *tt_error = tt_TooManyBooleans; + *Selectionlength = (short)(CurrentPtr-tempP)+1; + return tempP; + }; + CurrentPtr ++; + return CurrentPtr; +} + + +short TT_DeltaLevel( unsigned short opCode ); +short TT_DeltaLevel( unsigned short opCode ) +// register unsigned char opCode; +{ + short level; + + level = 0; + if ( opCode == 0x5d || opCode == 0x73 || opCode == 0x15d || opCode == 0x173) { + level = 0; + } else if ( opCode == 0x71 || opCode == 0x74 || opCode == 0x171 || opCode == 0x174) { + level = 1; + } else if ( opCode == 0x72 || opCode == 0x75 || opCode == 0x172 || opCode == 0x175) { + level = 2; + } + return level; +} + +void TT_CompileDelta( tt_deltaPType dArr[], short count, unsigned short insCode, short args[], short *argCount); +void TT_CompileDelta( tt_deltaPType dArr[], short deltaCount, unsigned short insCode, short args[], short *argCount ) +{ + short i, tmp, sCount, valid; + unsigned char argCode; + + valid = 1; + sCount = 0; + + for ( i = 0; i < deltaCount; i++) { + + argCode = (unsigned char) dArr[i].relppem; + + /* claudebe, already checked in the parser + if ( argCode < 0 || argCode > 15 ) { + *tt_error = tt_DeltaOORangePpem; + return; + } */ + + argCode <<= 4; + + /* in direct delta denominator == 1, otherwise we already tested that it is zero */ + + tmp = dArr[i].num; + + tmp += 8; + if ( tmp >= 8 ) tmp--; /* there is no code for a null deplacement */ + + /* DELTASHIFT in the DELTAxx command is constant 3, meaning delta from -8/8 to 8/8 by 1/8 steps + use direct delta DLTxx for other shift or base */ + + if (tmp > 15) tmp = 15; + if (tmp < 0) tmp = 0; + + argCode += tmp; + args[ sCount++ ] = argCode; + args[ sCount++ ] = dArr[i].point; + + } + /* claudebe 11/9/95, raid #73, DELTA without argument in PUSHOFF mode shouldn't produce any argument */ + if (deltaCount != 0) + args[ sCount++ ] = deltaCount; + *argCount = sCount; +} + +void TT_SortAndCombineDeltas(tt_deltaPType dArr[], short *countPtr ); +void TT_SortAndCombineDeltas(tt_deltaPType dArr[], short *countPtr ) +/* original Sampo code, except getting rid of the float and there is no need any more to test for zero div and allow delta of more than one pixel */ +{ + short i, j, change; + short count; + + count = *countPtr; + /* Now sort the deltas */ + for ( change = true; change; ) { + tt_deltaPType tmpDelta; + + change = false; + for ( i = count-2; i >= 0; i-- ) { + if ( (dArr[i].relppem > dArr[i+1].relppem) || ( dArr[i].relppem == dArr[i+1].relppem && dArr[i].point > dArr[i+1].point ) ) { + TT_memSwap( (char *)&dArr[i], (char *)&dArr[i+1], (char *)&tmpDelta, sizeof( tt_deltaPType ) ); + change = true; + } + } + } + + + /* Combine deltas */ + for ( change = true; change; ) { + change = false; + for ( i = count-1; i >= 0; i-- ) { + if (dArr[i].num == 0) { + /* we should almost flag this as an error */ + for ( j = i; j < count-1; j++ ) { + memcpy( (char *)&dArr[j], (char *)&dArr[j+1], sizeof( tt_deltaPType ) ); + } + change = true; + count--; + } else if ( i < (count-1) && dArr[i].relppem == dArr[i+1].relppem && dArr[i].point == dArr[i+1].point && dArr[i].denum == dArr[i+1].denum ) { + dArr[i].num += dArr[i+1].num; + for ( j = i+1; j < count-1; j++ ) { + memcpy( (char *)&dArr[j], (char *)&dArr[j+1], sizeof( tt_deltaPType ) ); + } + change = true; + count--; + } + } + } + *countPtr = count; +} + +short TT_IsDelta( unsigned short opCode ); +short TT_IsDelta( unsigned short opCode ) +// register unsigned char opCode; +{ + return ( opCode == 0x5d || ( opCode >= 0x71 && opCode <= 0x75) || opCode == 0x15d || (opCode >= 0x171 && opCode <= 0x175) ); +} + +short TT_IsPushBW( unsigned short opCode ); +short TT_IsPushBW( unsigned short opCode ) +// register unsigned char opCode; +{ + return ( ( opCode >= 0xB0 && opCode <= 0xBF) || opCode == 0x40 || opCode == 0x41 ); +} + +short TT_IsPushB( unsigned short opCode ); +short TT_IsPushB( unsigned short opCode ) +// register unsigned char opCode; +{ + return ( ( opCode >= 0xB0 && opCode <= 0xB7) || opCode == 0x40 ); +} + +short TT_IsNPushBW( unsigned short opCode ); +short TT_IsNPushBW( unsigned short opCode ) +// register unsigned char opCode; +{ + return ( opCode == 0x40 || opCode == 0x41 ); +} + +short TT_IsDirectDelta( unsigned short opCode ); +short TT_IsDirectDelta( unsigned short opCode ) +// register unsigned char opCode; +{ + return ( opCode == 0x15d || ( opCode >= 0x171 && opCode <= 0x175 ) ); +} + +short TT_IsDeltaC( unsigned short opCode ); +short TT_IsDeltaC( unsigned short opCode ) +// register unsigned char opCode; +{ + return ( (opCode >= 0x73 && opCode <= 0x75) || (opCode >= 0x173 && opCode <= 0x175) ); +} + +short TT_IsJR( unsigned short opCode ); +short TT_IsJR( unsigned short opCode ) +// register unsigned short opCode; +{ + return ( opCode == 0x1C || opCode == 0x78 || opCode == 0x79 ); +} + +short TT_IsBlockInstruction( unsigned short opCode ); +short TT_IsBlockInstruction( unsigned short opCode ) +// register unsigned char opCode; +{ + /* JR or IF, ELSE, FDEF , IDEF */ + return ( opCode == 0x1C || opCode == 0x78 || opCode == 0x79 || opCode == 0x2C || opCode == 0x58 || opCode == 0x89 || opCode == 0x1B ); +} + +short TT_IsIDEF_FDEFInstruction( unsigned short opCode ); +short TT_IsIDEF_FDEFInstruction( unsigned short opCode ) +// register unsigned char opCode; +{ + /* FDEF or IDEF */ + return ( opCode == 0x2C || opCode == 0x89 ); +} + +wchar_t * TT_DecodeDeltaP (wchar_t * CurrentPtr, wchar_t * EOLPtr, wchar_t * EndPtr, short InstructionIndex, unsigned short InstructionCode, short *deltaCount, tt_deltaPType dArr[], int32_t * Selectionlength, short * tt_error); +wchar_t * TT_DecodeDeltaP (wchar_t * CurrentPtr, wchar_t * EOLPtr, wchar_t * EndPtr, short InstructionIndex, unsigned short InstructionCode, short *deltaCount, tt_deltaPType dArr[], int32_t * SelectionLength, short * tt_error) +{ + short tempNum; + wchar_t * tempP; + unsigned char argCode; + *deltaCount = 0; + + /* opening bracket */ + if ( *CurrentPtr != L'[' /* ] for balance */ || CurrentPtr >= EOLPtr) + { + *tt_error = tt_DeltaListMissing; + *SelectionLength = 1; + return CurrentPtr; + }; + CurrentPtr++; + + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + while (CurrentPtr == EOLPtr && CurrentPtr < EndPtr) { + CurrentPtr++; + /* the DELTA command is an exception, the only command that can be spread over several lines, + the previous compiler was allowing this and users were using this, so I need to be backwards compatible! */ + EOLPtr = CurrentPtr + TT_GetLineLength(CurrentPtr, EndPtr); + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + } + + while ( /* [ for balance */ *CurrentPtr != L']' && CurrentPtr < EOLPtr) + { + if (*deltaCount > MAXDELTA) + { + *tt_error = tt_TooManyDeltas; + return CurrentPtr; + }; + + /* round bracket */ + if ( *CurrentPtr != L'(' /* ) for balance */ || CurrentPtr >= EOLPtr) + { + *tt_error = tt_DeltaOpeningParenthesisMissing; + *SelectionLength = 1; + return CurrentPtr; + }; + CurrentPtr++; + + tempP = CurrentPtr; + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); + + if (*tt_error != tt_NoError) return CurrentPtr; + + if (TT_IsDeltaC (InstructionCode)) + { + if (tempNum < asm_ppDescription1[6].lowestValidValue || tempNum > asm_ppDescription1[6].highestValidValue) + { + *tt_error = tt_CVTIndexOutOfRange; + *SelectionLength = (int32_t) (CurrentPtr - tempP); + return tempP; + } + } else { + if (tempNum < asm_ppDescription1[0].lowestValidValue || tempNum > asm_ppDescription1[0].highestValidValue) + { + *tt_error = tt_PointNbOutOfRange; + *SelectionLength = (int32_t) (CurrentPtr - tempP); + return tempP; + } + } + + dArr[*deltaCount].point = tempNum; + + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if ( *CurrentPtr != L'@' || CurrentPtr >= EOLPtr) + { + *tt_error = tt_DeltaAtSignMissing; + *SelectionLength = 1; + return CurrentPtr; + }; + CurrentPtr++; + + tempP = CurrentPtr; + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); + + if (*tt_error != tt_NoError) return CurrentPtr; + + if (TT_IsDirectDelta(InstructionCode)) + { + argCode = (unsigned char) tempNum; + } else { + argCode = (unsigned char)(tempNum - DELTABASE - 16 * TT_DeltaLevel( InstructionCode )); + } + + if ( argCode < 0 || argCode > 15 ) { + *tt_error = tt_PointSizeOutOfRange; + *SelectionLength = (int32_t) (CurrentPtr - tempP); + return tempP; + } + + + dArr[*deltaCount].relppem = argCode; + + tempP = CurrentPtr; + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); + + if (*tt_error != tt_NoError) return CurrentPtr; + + dArr[*deltaCount].num = tempNum; + + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + + if (TT_IsDirectDelta(InstructionCode)) + { + dArr[*deltaCount].denum = 1; + } else { + if ( *CurrentPtr != L'/' || CurrentPtr >= EOLPtr) + { + *tt_error = tt_DeltaDenominatorMissing; + *SelectionLength = 1; + return CurrentPtr; + }; + CurrentPtr++; + + tempP = CurrentPtr; + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &tempNum, SelectionLength, tt_error); + + if (*tt_error != tt_NoError) return CurrentPtr; + + if (tempNum != 8) /* ######## claudebe nobody should use a different denominator (DELTA command with fixed shift) */ + { + *tt_error = tt_DeltaWrongDenominator; + *SelectionLength = (int32_t) (CurrentPtr - tempP); + return tempP; + }; + + dArr[*deltaCount].denum = tempNum; + } + + + *deltaCount = *deltaCount + 1; + + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + /* round bracket */ + if /* ( for balance */ ( *CurrentPtr != L')' || CurrentPtr >= EOLPtr) + { + *tt_error = tt_DeltaClosingParenthesisMissing; + *SelectionLength = 1; + return CurrentPtr; + }; + CurrentPtr++; + + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + while (CurrentPtr == EOLPtr && CurrentPtr < EndPtr) { + CurrentPtr++; + /* the DELTA command is an exception, the only command that can be spread over several lines, + the previous compiler was allowing this and users were using this, so I need to be backwards compatible! */ + EOLPtr = CurrentPtr + TT_GetLineLength(CurrentPtr, EndPtr); + /* skip spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + } + } + + /* closing bracket */ + if ( /* [ for balance */ *CurrentPtr != L']' || CurrentPtr >= EOLPtr) + { + *tt_error = tt_DeltaClosingBracketMissing; + *SelectionLength = 1; + return CurrentPtr; + }; + CurrentPtr ++; + + /* Now sort the deltas */ + TT_SortAndCombineDeltas( dArr, deltaCount ); + return CurrentPtr; + +} + +wchar_t * TT_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned char InstructionCode, + asm_PushAndPopDescriptionType asm_ppDescription[], short pushOn, wchar_t * ArgTypeBuffer, short *argc,short *args,short *argc2,wchar_t *args2, + wchar_t ** LabelHandle, short * LabelLength, wchar_t ** BWLabelHandle, short * BWLabelLength, int32_t * SelectionLength, short * MaxFunctionDefs, + /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ + short * BinaryOffset, + /* for the DovMan partial compilation feature that flash points referenced by the current command */ + tt_flashingPoints * flashingPoints, + short * tt_error); +wchar_t * TT_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, unsigned char InstructionCode, + asm_PushAndPopDescriptionType asm_ppDescription[], short pushOn, wchar_t * ArgTypeBuffer, short *argc,short *args,short *argc2,wchar_t *args2, + wchar_t ** LabelHandle, short * LabelLength, wchar_t ** BWLabelHandle, short * BWLabelLength, int32_t * SelectionLength, short * MaxFunctionDefs, + /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ + short * BinaryOffset, + /* for the DovMan partial compilation feature that flash points referenced by the current command */ + tt_flashingPoints * flashingPoints, + short * tt_error) +{ + short argindex, argNb; + wchar_t * tempP = nullptr; + *argc = 0; + *argc2 = 0; + argindex = 0; + *LabelLength = 0; + *BWLabelLength = 0; + + if (*BinaryOffset == -1) + { + flashingPoints->NumberOfFlashingPoint = 0; + } + + if (pushOn || TT_IsPushBW(InstructionCode)) + { + argNb = (short)STRLENW(ArgTypeBuffer); + + if ( InstructionCode == 0x2A || InstructionCode == 0x2B ) + { /* function calls, we don't know what should be the number of arguments before processing */ + argNb = 255; + }; + + if (TT_IsPushBW(InstructionCode)) + { + if (TT_IsNPushBW(InstructionCode)) + { + argNb = 1; + ArgTypeBuffer[0] = L'A'; + ArgTypeBuffer[1] = 0; + + } else { + short k; + if ( InstructionCode >= 0xB0 && InstructionCode <= 0xB7) + { + /* PUSHB[] */ + argNb = InstructionCode - 0xB0 + 1; + for (k = 0; k < argNb; k++) + { + ArgTypeBuffer[k] = L'A'; + } + ArgTypeBuffer[k] = 0; + + } else { + /* PUSHW[] */ + argNb = InstructionCode - 0xB8 + 1; + for (k = 0; k < argNb; k++) + { + ArgTypeBuffer[k] = L'H'; + } + ArgTypeBuffer[k] = 0; + } + } + } + for (argindex=0;argindex= EOLPtr) break; + + if ( *CurrentPtr != L',' ) + { + break; /* it could be a comment */ + } + CurrentPtr = CurrentPtr + 1; + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if (CurrentPtr >= EOLPtr) break; + + if ( *CurrentPtr == L'*' ) + { + if (TT_IsPushBW(InstructionCode)) + { + *tt_error = tt_WildCardInPush; + return (CurrentPtr); + } + /* there is a wildcard */ + args2[argindex] = L'*'; + *argc2 = *argc2 + 1; + CurrentPtr++; + } else if ( (argNb != 255) && ( ArgTypeBuffer[argindex] == L'L' ) && (*CurrentPtr == L'#')) + { + int32_t StringLength; + CurrentPtr++; + /* we are looking for a label in a JR instruction */ + StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); + if ( StringLength == 0 ) + { + *tt_error = tt_VoidLabel; + return (CurrentPtr); + } + + if (StringLength >= MAXLABELLENGTH) + { + *tt_error = tt_LabelTooLong; + return (CurrentPtr); + } + + *LabelLength = (short)StringLength; + *LabelHandle = CurrentPtr; + + args2[argindex] = L'L'; /* L for Label */ + *argc2 = *argc2 + 1; + CurrentPtr = CurrentPtr + StringLength; + } else if ( (argNb != 255) && ( ArgTypeBuffer[argindex] == L'F' ) && (InstructionCode == 0x2C )) + { + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &args[argindex], SelectionLength, tt_error); + if (*tt_error != tt_NoError) return CurrentPtr; + /* function definition, we need to update MaxFunctionDefs */ + if (args[argindex] > *MaxFunctionDefs) *MaxFunctionDefs = args[argindex] ; + *argc = *argc + 1; + } else { + short foundPP, PPindex; + + /* there should be a number */ + tempP = CurrentPtr; + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &args[argindex], SelectionLength, tt_error); + if (*tt_error != tt_NoError) return CurrentPtr; + + if (argNb != 255) + { + if ( ArgTypeBuffer[argindex] == L'P' && *BinaryOffset == -1 && flashingPoints->NumberOfFlashingPoint < tt_MAXFLASHINGPOINTS) + { + flashingPoints->flashingPoints[flashingPoints->NumberOfFlashingPoint++] = args[argindex]; + } + foundPP = 0; /* Have not found it yet... */ + for (PPindex=0;(PPindex asm_ppDescription[PPindex].highestValidValue)) { + *SelectionLength = (int32_t) (CurrentPtr - tempP); + CurrentPtr = tempP; + if ( ArgTypeBuffer[argindex] == L'P' ) + { + *tt_error = tt_PointNbOutOfRange; + } else if ( ArgTypeBuffer[argindex] == L'I' ) + { + *tt_error = tt_CVTIndexOutOfRange; + } else if ( ArgTypeBuffer[argindex] == L'J' ) + { + *tt_error = tt_StorageIndexOutOfRange; + } else if ( ArgTypeBuffer[argindex] == L'C' ) + { + *tt_error = tt_ContourNbOutOfRange; + } else { + *tt_error = tt_ArgumentOutOfRange; + }/*if */ + } /* if */ + } /*for */ + } /*for*/ + + if ( ArgTypeBuffer[argindex] == L'A' && argindex == 0) + /* we are in a NPUSH and know now the amount of arguments */ + { + short k; + argNb = argNb + args[argindex]; + if ( InstructionCode == 0x40 ) + { + /* NPUSHB[] */ + for (k = 1; k <= args[argindex]; k++) + { + ArgTypeBuffer[k] = L'A'; + } + ArgTypeBuffer[k] = 0; + + } else { + /* NPUSHW[] */ + for (k = 1; k <= args[argindex]; k++) + { + ArgTypeBuffer[k] = L'H'; + } + ArgTypeBuffer[k] = 0; + } + } + } /*if*/ + + *argc = *argc + 1; + if (*tt_error != tt_NoError) return CurrentPtr; + } + + + } + + + if ( InstructionCode == 0x2A || InstructionCode == 0x2B ) + { /* function calls, we don't know waht should be the number of arguments before processing */ + /* we need to check the function number, + FUTURE : we may want to check the function arguments here */ + short foundPP, PPindex; + + if ((InstructionCode == 0x2B && argindex < 1) || (InstructionCode == 0x2A && argindex < 2) ) + { + *tt_error = tt_MissingParameters; + return CurrentPtr; + } + + if (args2[argindex-1] != L'*') + /* check the function number */ + { + foundPP = 0; /* Have not found it yet... */ + for (PPindex=0;(PPindex asm_ppDescription[PPindex].highestValidValue)) { + *tt_error = tt_FunctionNbOutOfRange; + *SelectionLength = (int32_t) (CurrentPtr - tempP); + CurrentPtr = tempP; + } /* if */ + } /* if */ + } /*for */ + } + + } else { + if (argindex < (short)STRLENW(ArgTypeBuffer)) + { + *tt_error = tt_MissingParameters; + return CurrentPtr; + } + } + + return CurrentPtr; + + } else { + + if (TT_IsJR(InstructionCode)) + { + int32_t StringLength; + /* in this case only jump command has parameters */ + + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if ( *CurrentPtr++ != L',' || CurrentPtr >= EOLPtr ) + { + *tt_error = tt_EmptyParameterList; + return (CurrentPtr-1); + } + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if ( *CurrentPtr++ != L'(' || (CurrentPtr >= EOLPtr) ) + { + *tt_error = tt_JRExpectingABracket; + return (CurrentPtr-1); + } + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + /* parse the Bn or Wn label */ + if ( (*CurrentPtr != L'B' && *CurrentPtr != L'W') || (CurrentPtr >= EOLPtr) ) + { + *tt_error = tt_JRExpectingABWLabel; + return (CurrentPtr); + } + + StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); + if (StringLength < 2) + { + *tt_error = tt_VoidLabel; + return (CurrentPtr); + } + + if (StringLength >= MAXLABELLENGTH) + { + *tt_error = tt_LabelTooLong; + return (CurrentPtr); + } + + + *BWLabelLength = (short)StringLength; + *BWLabelHandle = CurrentPtr; + + CurrentPtr = CurrentPtr + StringLength; + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if ( *CurrentPtr++ != L'=' || (CurrentPtr >= EOLPtr) ) + { + *tt_error = tt_JRExpectingAEqual; + return (CurrentPtr-1); + } + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if ( *CurrentPtr++ != L'#' || (CurrentPtr >= EOLPtr) ) + { + *tt_error = tt_JRExpectingALabel; + return (CurrentPtr-1); + } + + + StringLength = TT_GetStringLength (CurrentPtr, EOLPtr); + if (StringLength >= MAXLABELLENGTH) + { + *tt_error = tt_LabelTooLong; + *SelectionLength = StringLength; + return (CurrentPtr); + } + + if ( StringLength == 0 ) + { + *tt_error = tt_VoidLabel; + *SelectionLength = StringLength; + return (CurrentPtr); + } + + *LabelLength = (short)StringLength; + *LabelHandle = CurrentPtr; + + CurrentPtr = CurrentPtr + StringLength; + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if ( *CurrentPtr != L')' || (CurrentPtr >= EOLPtr) ) + { + *tt_error = tt_JRExpectingABracket; + return (CurrentPtr-1); + } + CurrentPtr++; + + } + return CurrentPtr; + } +} + +void TT_StoreArgumentsAndInstruction (unsigned char InstructionCode, short ** aHandle, unsigned char ** iHandle, + short argc, short *args, short argc2,wchar_t *args2, short * tt_error); + +void TT_StoreArgumentsAndInstruction (unsigned char InstructionCode, short ** aHandle, unsigned char ** iHandle, + short argc, short *args, short argc2,wchar_t *args2, short * tt_error) +{ + short TotalArg, j; + + TotalArg = argc + argc2; + + **iHandle = InstructionCode; + *iHandle = *iHandle +1; + + if (TT_IsPushBW(InstructionCode)) + { + /* PUSH arguments goes to the instruction stream */ + short index = 0; + if (TT_IsNPushBW(InstructionCode)) + { + /* number of bytes to push */ + **iHandle = (unsigned char)args[index++]; + *iHandle = *iHandle +1; + + } + if (TT_IsPushB(InstructionCode)) + { + /* arguments are bytes */ + for ( j = index; j < TotalArg; j++ ) { + **iHandle = (unsigned char) args[index++]; + *iHandle = *iHandle +1; + } + } else { + /* arguments are words */ + for ( j = index; j < TotalArg; j++ ) { + short word = args[index++]; + *(short *) (*iHandle) = SWAPW(word); + *iHandle = *iHandle +2; + } + } + + } else { + for ( j = 0; j < TotalArg; j++ ) { + short index; + index = TotalArg - 1 - j; + if ( index < 0 || index > 255 ) { + * tt_error = tt_ArgumentIndexOutOfRange; + } + + if ( args2[index] != L'*' ) + { + // REVIEW: mjan - Do we need to think about byte order here? + // B.St. It doesn't appear to matter, since we're incrementing aHandle by 1 byte at a time + **aHandle = args[index]; /* Reverse since we are going to reverse everything later */ + *aHandle = *aHandle +1; + } + } + } + + +} + + +/* + * + */ +short TT_BytePush( short argStore[], short StartIndex, short numberofArgs, unsigned char *uCharP , tt_PStype * PS); +short TT_BytePush(short argStore[], short StartIndex, short numberofArgs,unsigned char * uCharP ,tt_PStype * PS ) +// short argStore[], numberofArgs; +// register unsigned char *uCharP; +{ + short i, k; + short count; + + count = 0; + if ( numberofArgs <= 8 ) { + uCharP[count++] = 0xB0 + numberofArgs -1;/* PUSHB */ + } else { + uCharP[count++] = 0x40; /* NPUSHB */ + uCharP[count++] = (unsigned char) numberofArgs; + } + if (PS != NULL) + /* fill the iPos value for the Bn PUSH labels of the current PUSH */ + { + for ( k = PS->num-1; k >=0; k--) + { + if ((PS->ps[k]->aPtr == NULL) && (PS->ps[k]->LocalIndex >= StartIndex) && (PS->ps[k]->LocalIndex < StartIndex+numberofArgs) ) + { + PS->ps[k]->aPtr = (unsigned char *) (uCharP + count + PS->ps[k]->LocalIndex - StartIndex); + } + } + } + for ( i = 0; i < numberofArgs; i++ ) { + uCharP[count++] = (unsigned char) argStore[StartIndex+i]; + } + return count; +} + +/* + * + */ +short TT_WordPush( short argStore[], short StartIndex, short numberofArgs, unsigned char *uCharP, tt_PStype * PS ); +short TT_WordPush( short argStore[], short StartIndex ,short numberofArgs,unsigned char * uCharP, tt_PStype * PS ) +// short argStore[], numberofArgs; +// register unsigned char *uCharP; +{ + short i,k; + short count; + short *shortP; + + count = 0; + if ( numberofArgs <= 8 ) { + uCharP[count++] = 0xB8 + numberofArgs -1;/* PUSHW */ + } else { + uCharP[count++] = 0x41; /* NPUSHW */ + uCharP[count++] = (unsigned char) numberofArgs; + } + + if (PS != NULL) + /* fill the iPos value for the Wn PUSH labels of the current PUSH */ + { + for ( k = PS->num-1; k >=0; k--) + { + if ((PS->ps[k]->aPtr == 0) && (PS->ps[k]->LocalIndex >= StartIndex) && (PS->ps[k]->LocalIndex < StartIndex+numberofArgs) ) + { + PS->ps[k]->aPtr = (unsigned char *)(uCharP + count + 2 * (PS->ps[k]->LocalIndex - StartIndex)); + } + } + } + + for ( i = 0; i < numberofArgs; i++ ) { + shortP = (short *)&uCharP[count]; + *shortP = SWAPW(argStore[StartIndex+i]); + count += 2; + } + return count; +} + +short TT_ByteRunLength( short *args, short n ); +/* + * + */ +short TT_ByteRunLength(short * args,short n ) +//short *args, n; +{ + short len; + + for ( len = 0; len < n && *args >= 0 && *args <= 255; args++ ) { + len++; + } + return len; +} + +short TT_OptimizingPushArguments( unsigned char * BinaryOut,unsigned char * BinaryOutMaxPtr, short argStore[], short numberofArgs, tt_PStype * PS, short *tt_error); +short TT_OptimizingPushArguments( unsigned char * BinaryOut,unsigned char * BinaryOutMaxPtr, short argStore[], short numberofArgs, tt_PStype * PS, short *tt_error) +{ + short i, count, argument; + short argCount, n, byteRun, runLength; + short doBytePush, limit; + + + count = 0; + + for ( n = 0; numberofArgs > 0; ) { + argCount = numberofArgs > 255 ? 255 : numberofArgs; + doBytePush = true; + + for ( runLength = 0, i = n; i < (n + argCount); i++ ) { + argument = argStore[i]; + byteRun = TT_ByteRunLength( &argStore[i], (short) (n + argCount - i) ); + + /* if we have a run of bytes of length 3 (2 if first or last) or more it is more + optimal in terms of space to push them as bytes */ + limit = 3; + if ( (runLength == 0) || ((byteRun + i) >= (n + argCount)) ) { + limit = 2; + } + if ( byteRun >= limit ) { + if ( runLength > 0 ) { + argCount = runLength; + doBytePush = false; + break; /*****/ + } else { + argCount = byteRun; + doBytePush = true; + break; /*****/ + } + } + + if ( argument > 255 || argument < 0 ) doBytePush = false; + runLength++; + } + numberofArgs -= argCount; + + + + if ( doBytePush ) { + if (BinaryOut + count + 2 + argCount > BinaryOutMaxPtr) + { + *tt_error = tt_ProgramTooBig; + return 0; + } + count += TT_BytePush( argStore, n, argCount, &BinaryOut[count], PS ); + } else { + if (BinaryOut + count + 2 + 2 *argCount > BinaryOutMaxPtr) + { + *tt_error = tt_ProgramTooBig; + return 0; + } + count += TT_WordPush( argStore, n, argCount, &BinaryOut[count], PS ); + } + n += argCount; + } + return count; +} + + +short TT_WriteOutBlock( unsigned char *BinaryOut,unsigned char *BinaryOutEndPtr, short * BinaryOffset, short AddOffset, short argStore[], + unsigned char insStore[], short numberofArgs, short numberofInstructions, short * tt_error); +short TT_WriteOutBlock( unsigned char *BinaryOut,unsigned char *BinaryOutEndPtr, short * BinaryOffset, short AddOffset, short argStore[], + unsigned char insStore[], short numberofArgs, short numberofInstructions, short * tt_error) +{ + short i, j, k; + short count; + + /* Reverse arguments since we are pushing them */ + k = numberofArgs >> 1; + for ( i = 0; i < k; i++ ) { + j = argStore[i]; + argStore[i] = argStore[numberofArgs-1-i]; + argStore[numberofArgs-1-i] = j; + } + + count = TT_OptimizingPushArguments(BinaryOut, BinaryOutEndPtr, argStore, numberofArgs, (tt_PStype *) NULL, tt_error); + + if (AddOffset && *BinaryOffset != -1) + { + *BinaryOffset = *BinaryOffset + count; + } + + for ( i = 0; i < numberofInstructions; i++ ) { + BinaryOut[ count++ ] = insStore[i]; + } + + return count; +} + +void TT_ResetLastCommandCompilationStatus(tt_CompilationStatus *CompilationStatus); +void TT_ResetLastCommandCompilationStatus(tt_CompilationStatus *CompilationStatus) +{ + CompilationStatus->LastCommandWasAnIF = false; + CompilationStatus->LastCommandWasAnELSE = false; + CompilationStatus->LastCommandWasAnFDEF = false; + CompilationStatus->LastCommandWasAnIDEF = false; + CompilationStatus->LastCommandWasAnJUMP = false; + CompilationStatus->LastCommandWasAnEND = false; +} + +void TT_CheckAndUpdateCompilationStatus(short InstructionCode,tt_CompilationStatus *CompilationStatus,short * tt_error); +void TT_CheckAndUpdateCompilationStatus(short InstructionCode,tt_CompilationStatus *CompilationStatus,short * tt_error) +{ + + if (CompilationStatus->WeAreInPushOnMode) + { + if (CompilationStatus->LastCommandWasAnIF || CompilationStatus->LastCommandWasAnELSE || CompilationStatus->LastCommandWasAnFDEF || + CompilationStatus->LastCommandWasAnIDEF || CompilationStatus->LastCommandWasAnJUMP) + { + *tt_error = tt_ExpectingaBEGIN; + return; + } + } + TT_ResetLastCommandCompilationStatus(CompilationStatus); + + switch ( InstructionCode ) { + case 0x58: /* IF */ + { + CompilationStatus->WeAreInsideAnIF = true; + CompilationStatus->LastCommandWasAnIF = true; + CompilationStatus->NumberOfEmbeddedIF ++; + if (CompilationStatus->NumberOfEmbeddedIF > MAXIFRECURSION) + { + *tt_error = tt_TooManyEmbeddedIF; + return; + } + CompilationStatus->ELSEStatus[CompilationStatus->NumberOfEmbeddedIF] = false; + break; + } + case 0x59: /* EIF */ + { + if ((!CompilationStatus->WeAreInsideAnIF) || (CompilationStatus->NumberOfEmbeddedIF == 0)) + { + *tt_error = tt_EIFwithoutIF; + return; + } + + CompilationStatus->NumberOfEmbeddedIF --; + if (CompilationStatus->NumberOfEmbeddedIF == 0) CompilationStatus->WeAreInsideAnIF = false; + break; + } + case 0x1B: /* ELSE */ + { + if ((!CompilationStatus->WeAreInsideAnIF) || (CompilationStatus->NumberOfEmbeddedIF == 0)) + { + *tt_error = tt_ELSEwithoutIF; + return; + } + if (CompilationStatus->ELSEStatus[CompilationStatus->NumberOfEmbeddedIF]) + { + *tt_error = tt_ELSEwithinELSE; + return; + } + CompilationStatus->ELSEStatus[CompilationStatus->NumberOfEmbeddedIF] = true; + CompilationStatus->LastCommandWasAnELSE = true; + break; + } + case 0x2C: /* FDEF */ + { + if (CompilationStatus->WeAreInsideAnFDEF) + { + *tt_error = tt_FDEFInsideFDEF; + return; + } + if (CompilationStatus->WeAreInsideAnIDEF) + { + *tt_error = tt_FDEFInsideIDEF; + return; + } + CompilationStatus->WeAreInsideAnFDEF = true; + CompilationStatus->LastCommandWasAnFDEF = true; + break; + } + case 0x89: /* IDEF */ + { + if (CompilationStatus->WeAreInsideAnIDEF) + { + *tt_error = tt_IDEFInsideIDEF; + return; + } + if (CompilationStatus->WeAreInsideAnFDEF) + { + *tt_error = tt_IDEFInsideFDEF; + return; + } + CompilationStatus->WeAreInsideAnFDEF = true; + CompilationStatus->LastCommandWasAnFDEF = true; + break; + } + case 0x2D: /* ENDF */ + { + if (!CompilationStatus->WeAreInsideAnFDEF && !CompilationStatus->WeAreInsideAnIDEF) + { + *tt_error = tt_ENDFwithoutFDEF; + return; + } + + CompilationStatus->WeAreInsideAnFDEF = false; + CompilationStatus->WeAreInsideAnIDEF = false; + break; + } + } + + CompilationStatus->LastCommandWasAnEND = false; + +//typedef struct { +// short WeAreInsideAnIF; +// short WeAreInsideTheELSEofAnIF; +// short WeAreInsideAnFDEF; +// short WeAreInsideAnIDEF; +// short WeAreInPushOnMode; +// short WeAreInPrePushMode; +// short WeAreInsideGHBlock; +// short LastCommandWasAnIF; /* used to check for #BEGIN (beginning of a block) */ +// short LastCommandWasAnELSE; /* used to check for #BEGIN (beginning of a block) */ +// short LastCommandWasAnFDEF; /* used to check for #BEGIN (beginning of a block) */ +// short LastCommandWasAnIDEF; /* used to check for #BEGIN (beginning of a block) */ +// short LastCommandWasAnEND; /* to check if the ENDF, ELSE or EIF is preceeded by #END (end of block) */ +//} tt_CompilationStatus; + +} + +wchar_t *TT_SkipEmptyLines( wchar_t *p, wchar_t *endP,short *pLineCount ); +wchar_t *TT_SkipEmptyLines( wchar_t *p, wchar_t *endP,short *pLineCount ) +{ + wchar_t *myP; + + /* skip spaces and empty lines, keep track of the line number */ + for ( myP = 0; myP != p; ) { + myP = p; + while ( *p == L' ' && p < endP) + p++; + while ( (*p == L'\x0D' || *p == L'\x0A') && p < endP) + { + *pLineCount = *pLineCount+1; p++; + } + } + return p; +} + +wchar_t *TT_SkipCommentsAndEmptyLines( wchar_t *p, wchar_t *endP,short *lineCount, short * error ); +wchar_t *TT_SkipCommentsAndEmptyLines( wchar_t *p, wchar_t *endP,short *pLineCount, short * error ) +{ + wchar_t * tempP; + *error = tt_NoError; + p = TT_SkipEmptyLines( p, endP,pLineCount); + + while ( *p == L'/' && *(p+1) == L'*' && p < endP ) { /* skip C style comments */ + tempP = p; + p++; p++; + while ( !(*p == L'*' && *(p+1) == L'/') && p < endP ) { + + if ( *p == L'/' && *(p+1) == L'*' ) { + *error = tt_EmbeddedComment; + return p; + } + if ( *p == '\x0D' || *p == '\x0A') + { + *pLineCount = *pLineCount+1; + } + p++; + + } + if (p == endP) { + *error = tt_UnterminatedComment; + return tempP; /* return the pointer to the beginning of the unterminated comment */ + } + p++; p++; + p = TT_SkipEmptyLines( p, endP,pLineCount); + } + return p; +} + +wchar_t *TT_InnerCompile( + /* source text, pointer to the begining, the end and the cursor position (to be able to trace until that line) */ + wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, + /* pointer to the output buffer, it's maximal length and return the Binary length */ + unsigned char * BinaryOut, char * BinaryOutEndPtr, int32_t * BinaryLength, + /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ + short * BinaryOffset, + /* length of the text to be selected in case of error */ + int32_t * SelectionLength, + /* line number where the first error occur */ + short * ErrorLineNb, + /* return : approximate stack need, higher function number */ + short * StackNeed, short * MaxFunctionDefs, + /* pass the value 1 to the recursion level, used internally to treat recursively embedded #BEGIN #END blocks */ + short RecursionLevel, + tt_CompilationStatus *CompilationStatus, + /* for the DovMan partial compilation feature that flash points referenced by the current command */ + tt_flashingPoints * flashingPoints, + ASMType asmType, /* asmGLYF, asmPREP, or asmFPGM */ + /* error code, return tt_NoError if no error */ + short * tt_error); + +wchar_t *TT_InnerCompile( + /* source text, pointer to the begining, the end and the cursor position (to be able to trace until that line) */ + wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, + /* pointer to the output buffer, it's maximal length and return the Binary length */ + unsigned char * BinaryOut, char * BinaryOutEndPtr, int32_t * BinaryLength, + /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ + short * BinaryOffset, + /* length of the text to be selected in case of error */ + int32_t * SelectionLength, + /* line number where the first error occur */ + short * ErrorLineNb, + /* return : approximate stack need, higher function number */ + short * StackNeed, short * MaxFunctionDefs, + /* pass the value 1 to the recursion level, used internally to treat recursively embedded #BEGIN #END blocks */ + short RecursionLevel, + tt_CompilationStatus *CompilationStatus, + /* for the DovMan partial compilation feature that flash points referenced by the current command */ + tt_flashingPoints * flashingPoints, + ASMType asmType, /* asmGLYF, asmPREP, or asmFPGM */ + /* error code, return tt_NoError if no error */ + short * tt_error) +{ + short LineNb, LastLineCompiled; + int32_t LineLength, SLoopLineLength; + wchar_t *CurrentPtr, *SLoopPtr; + short numberofArgs, numberofInstructions; + short *argStore, *aPtr; + unsigned char *insStore, *iPtr; + short NeedTwoPass = false; /* used to be MyCode */ + short ghtblock ; + short loop; + short args[256], argc, argc2; + wchar_t args2[256]; + wchar_t argTypeBuffer[ARGTYPEBUFFER_SIZE]; + tt_LabelType * Label; + tt_JRtype * JRList; + tt_PStype *PushLabels; + tt_JrBWtype *JrBW; + short AddOffset = false; + unsigned char * InnerBinaryOutMaxPtr,*InnerArgStoreMaxPtr; + + + CompilationStatus->WeAreInPushOnMode = true; /* reset after a #BEGIN, to be backwards compatible */ + CompilationStatus->WeAreInPrePushMode = true; + CompilationStatus->WeAreInsideGHBlock = false; + CompilationStatus->LastCommandWasAnIF = false; + CompilationStatus->LastCommandWasAnELSE = false; + CompilationStatus->LastCommandWasAnFDEF = false; + CompilationStatus->LastCommandWasAnIDEF = false; + CompilationStatus->LastCommandWasAnJUMP = false; + CompilationStatus->LastCommandWasAnEND = false; + + *tt_error = 0; + *BinaryLength = 0; + *SelectionLength = 0; + *tt_error = tt_NoError; + SLoopPtr = StartPtr; + SLoopLineLength = 0; + + + if (RecursionLevel == 1) + { + *BinaryOffset = -1; + AddOffset = true; + } + if (*BinaryOffset == -1) AddOffset = true; + + PushLabels = (tt_PStype *) NewP( sizeof(tt_PStype) ); + if ( PushLabels == NULL) { + *tt_error = tt_NotEnoughMemory; + *SelectionLength = 0; + return StartPtr; + } + + Label = (tt_LabelType *) NewP( sizeof(tt_LabelType) ); + if ( Label == NULL) { + *tt_error = tt_NotEnoughMemory; + *SelectionLength = 0; + DisposeP((void**)&PushLabels); + return StartPtr; + } + + JRList = (tt_JRtype *) NewP( sizeof(tt_JRtype) ); + if ( JRList == NULL) { + *tt_error = tt_NotEnoughMemory; + *SelectionLength = 0; + DisposeP((void**)&Label); + DisposeP((void**)&PushLabels); + return StartPtr; + } + + JrBW = (tt_JrBWtype *) NewP( sizeof(tt_JrBWtype) ); + if ( JrBW == NULL) { + *tt_error = tt_NotEnoughMemory; + *SelectionLength = 0; + DisposeP((void**)&Label); + DisposeP((void**)&PushLabels); + DisposeP((void**)&JRList); + return StartPtr; + } + + Label->num = 0; + JRList->num = 0; + PushLabels->num = 0; + JrBW->num = 0; + + argStore = (short *)NewP( sizeof(short) * (MAXARGUMENTS) ); + if ( argStore == 0 ) { + *tt_error = tt_NotEnoughMemory; + *SelectionLength = 0; + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return StartPtr; + } + insStore = (unsigned char *)NewP( sizeof(char) * (MAXINSTRUCTIONCOUNT) ); + if ( insStore == 0 ) { + *tt_error = tt_NotEnoughMemory; + DisposeP((void**)&argStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return StartPtr; + } + aPtr = argStore; iPtr = insStore; /* set up pointers */ + InnerBinaryOutMaxPtr = (unsigned char *)(insStore + MAXINSTRUCTIONCOUNT); + InnerArgStoreMaxPtr = (unsigned char *)((unsigned char*)argStore + MAXARGUMENTS); + + CurrentPtr = StartPtr; + LastLineCompiled = 0; + ghtblock = false; + loop = 1; + + for ( LineNb = 1; CurrentPtr <= EndPtr && *tt_error == tt_NoError; ) { + short LineInc = 0; + CurrentPtr = TT_SkipCommentsAndEmptyLines( CurrentPtr,EndPtr, &LineInc, tt_error ); + LineNb += LineInc; + *ErrorLineNb += LineInc; + + if ( *tt_error != tt_NoError) + { + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + + *SelectionLength = 2; + return CurrentPtr; + }; + + if ( CurrentPtr == EndPtr) break; + LineLength = TT_GetLineLength(CurrentPtr, EndPtr); + + if (LineNb == LastLineCompiled) + { + *tt_error = tt_TwoInstructionsInSameLine; + *SelectionLength = LineLength; + break; + } + LastLineCompiled = LineNb; + + if (*CurrentPtr == L'#') + /* label or compiler switch */ + { + int32_t StringLength; + + StringLength = TT_GetStringLength (CurrentPtr +1, EndPtr); + if ((*(CurrentPtr + StringLength + 1) == L':') && (CurrentPtr + StringLength + 1 != EndPtr)) + /* this is a label */ + { + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { + numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); + numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); + + if (StringLength >= MAXLABELLENGTH) + { + *tt_error = tt_LabelTooLong; + *SelectionLength = LineLength; + break; + } + + if ( StringLength == 0 ) + { + *tt_error = tt_VoidLabel; + *SelectionLength = LineLength; + break; + } + + TT_SaveLabel( numberofArgs, numberofInstructions, StringLength, CurrentPtr+1, Label, tt_error); + + if ( *tt_error != tt_NoError) + { + *SelectionLength = LineLength; + break; + } + CurrentPtr = CurrentPtr + StringLength+2; /* the # and the : */ + } + } else { + /* this is a compiler switch */ + short i, found, SwitchCode = 0; + for ( found = i = 0; i < (short)TOTALNUMBEROFSWITCH; i++ ) { + if ( StringLength == (short) STRLENW( tt_CompilerSwitch[i].name ) && + wcsncmp( CurrentPtr+1, tt_CompilerSwitch[i].name, StringLength) == 0 ) { + + found = true; + SwitchCode = tt_CompilerSwitch[i].index; + } + }; + if (!found) { + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { + *tt_error = tt_UnknownSwitch; + *SelectionLength = StringLength + 1; + } + } else + { + switch ( SwitchCode ) { + case tt_Push_Switch: + { + wchar_t * EOLPtr; + short mycount; + + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { + short localArgStore[PUSH_ARG], localNumberofArgs; + EOLPtr = CurrentPtr + LineLength; + + TT_ResetLastCommandCompilationStatus(CompilationStatus); + CurrentPtr = CurrentPtr + StringLength+1; + + CurrentPtr = TT_ParsePUSHandSave( PushLabels, CurrentPtr, EOLPtr, localArgStore, &localNumberofArgs, SelectionLength,tt_error ); + + mycount = TT_OptimizingPushArguments(iPtr, InnerBinaryOutMaxPtr, localArgStore, localNumberofArgs,PushLabels, tt_error ); + + numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ + if (localNumberofArgs > numberofArgs) + /* numberofArgs represent what will already be popped from the stack at this point */ + *StackNeed = *StackNeed + localNumberofArgs - numberofArgs; + iPtr += mycount; + } + + break; + } + case tt_PushOn_Switch: + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { +/* should be a warning, too many errors on old fonts */ +// if (CompilationStatus->WeAreInPushOnMode) +// { +// *tt_error = tt_PUSHONwhenAlreadyOn; +// } + CompilationStatus->WeAreInPushOnMode = true; + CurrentPtr = CurrentPtr + StringLength+1; + } + break; + case tt_PushOff_Switch: + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { +// if (!CompilationStatus->WeAreInPushOnMode) +// { +// *tt_error = tt_PUSHOFFwhenAlreadyOff; +// } + if (CompilationStatus->WeAreInPushOnMode) + { + if (CompilationStatus->LastCommandWasAnIF || CompilationStatus->LastCommandWasAnELSE || CompilationStatus->LastCommandWasAnFDEF || + CompilationStatus->LastCommandWasAnIDEF || CompilationStatus->LastCommandWasAnJUMP) + { + *tt_error = tt_ExpectingaBEGIN; + } + } + CompilationStatus->WeAreInPushOnMode = false; + CurrentPtr = CurrentPtr + StringLength+1; + } + break; + case tt_Begin_Switch: + { + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { + int32_t count; + short subStackNeed, tempInsOffset; + tt_CompilationStatus SavedCompilationStatus; + + if (RecursionLevel == MAXBLOCKEMBEDDING) + { + *tt_error = tt_TooManyEnbeddedBlocks; + *SelectionLength = StringLength + 1; + break; + } + TT_ResetLastCommandCompilationStatus(CompilationStatus); + CurrentPtr = CurrentPtr + StringLength+1; + subStackNeed = 0; + + tempInsOffset = 0; + if (*BinaryOffset == -1) tempInsOffset = (short)(ptrdiff_t)(iPtr - insStore); + + SavedCompilationStatus = *CompilationStatus; + CurrentPtr = TT_InnerCompile(CurrentPtr, EndPtr,SelStartPtr, iPtr, (char *)InnerBinaryOutMaxPtr, &count, BinaryOffset, SelectionLength, ErrorLineNb, + &subStackNeed, MaxFunctionDefs, RecursionLevel+1,CompilationStatus, flashingPoints, asmType, tt_error) ; + + /* restore the status that need to be restored */ + CompilationStatus->WeAreInPushOnMode = SavedCompilationStatus.WeAreInPushOnMode; + + if (*tt_error == tt_NoError) + { + if (CompilationStatus->NumberOfEmbeddedIF != SavedCompilationStatus.NumberOfEmbeddedIF) + { + *tt_error = tt_IFgoingAcrossBlocks; + CurrentPtr = CurrentPtr - 4; /* go back before the #END */ + *SelectionLength = 4; + } + if (CompilationStatus->WeAreInsideAnFDEF != SavedCompilationStatus.WeAreInsideAnFDEF) + { + *tt_error = tt_FDEFgoingAcrossBlocks; + CurrentPtr = CurrentPtr - 4; /* go back before the #END */ + *SelectionLength = 4; + } + if (CompilationStatus->WeAreInsideAnIDEF != SavedCompilationStatus.WeAreInsideAnIDEF) + { + *tt_error = tt_IDEFgoingAcrossBlocks; + CurrentPtr = CurrentPtr - 4; /* go back before the #END */ + *SelectionLength = 4; + } + } + + + if (*BinaryOffset != -1) *BinaryOffset = *BinaryOffset + tempInsOffset; + + numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ + if (subStackNeed > numberofArgs) + /* numberofArgs represent what will already be popped from the stack at this point */ + *StackNeed = *StackNeed + subStackNeed - numberofArgs; + + iPtr += count; + } + break; + } + case tt_End_Switch: + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { + TT_ResetLastCommandCompilationStatus(CompilationStatus); + if (RecursionLevel > 1) + { + CurrentPtr = CurrentPtr + StringLength+1; + /* write the code of the block before comming back */ + numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ + numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); /* number of instructions */ + + + if (NeedTwoPass) + { + CurrentPtr = TT_FindLabelError(PushLabels, JrBW, JRList, Label, CurrentPtr, SelectionLength, tt_error) ; + + if (*tt_error != tt_NoError) + { + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; + }; + TT_JRpushON_ReplaceLabel( JRList, Label, argStore, tt_error); + TT_JRpushOFF_ReplaceLabel( JrBW, PushLabels, Label, tt_error); + } + + if (*tt_error == tt_NoError) + *BinaryLength = TT_WriteOutBlock( BinaryOut, (unsigned char *)BinaryOutEndPtr, BinaryOffset, AddOffset, argStore, insStore, numberofArgs, numberofInstructions, tt_error); + + *StackNeed = *StackNeed + numberofArgs; + + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; + } else { + *tt_error = tt_EndWithoutBegin; + *SelectionLength = StringLength + 1; + } + } + break; + case tt_GHTBlockBegin_Switch: + CompilationStatus->WeAreInsideGHBlock = true; + CurrentPtr = CurrentPtr + LineLength; + break; + case tt_GHTBlockEnd_Switch: + CompilationStatus->WeAreInsideGHBlock = false; + CurrentPtr = CurrentPtr + LineLength; + break; + } + } + } + } else { + if (CompilationStatus->WeAreInsideGHBlock) + { + CurrentPtr = CurrentPtr + LineLength; + } else { + /* regular instruction */ + short InstructionIndex, found; + unsigned short InstructionCode = 0; /* unsigned short because of the fake code used to detect composite commands */ + int32_t StringLength; + + StringLength = TT_GetStringLength (CurrentPtr, EndPtr); + + for ( found = InstructionIndex = 0; InstructionIndex < (short)TOTALNUMBEROFINSTRUCTIONS; InstructionIndex++ ) { + if ( StringLength == (short) STRLENW( tt_instruction[InstructionIndex].name ) && + wcsncmp(CurrentPtr, tt_instruction[InstructionIndex].name, StringLength) == 0 ) { + found = true; + InstructionCode = tt_instruction[InstructionIndex].baseCode; + break; + } + } + if (!found) { + *SelectionLength = StringLength; + *tt_error = tt_UnknownInstruction; + } else { + TT_CheckAndUpdateCompilationStatus(InstructionCode,CompilationStatus,tt_error); + if ( *tt_error != tt_NoError) + { + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + *SelectionLength = StringLength; + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; + }; + + if (TT_IsDelta(InstructionCode)) + { + wchar_t * EOLPtr, *LineBegin; + short deltaCount; + short i; + + tt_deltaPType dArr[MAXDELTA]; + LineBegin = CurrentPtr; + EOLPtr = CurrentPtr + LineLength; + /* this is a delta instruction */ + CurrentPtr = TT_DecodeDeltaP( CurrentPtr+StringLength, EOLPtr, EndPtr, InstructionIndex, InstructionCode, &deltaCount, dArr, SelectionLength,tt_error); + + if ((deltaCount == 0) && CompilationStatus->WeAreInPushOnMode && *tt_error == tt_NoError) + { + *tt_error = tt_DELTAWithoutArguments; + *SelectionLength = LineLength; + return LineBegin; + } + if ((deltaCount != 0) && !CompilationStatus->WeAreInPushOnMode && *tt_error == tt_NoError) + { + *tt_error = tt_DELTAWithArguments; + *SelectionLength = LineLength; + return LineBegin; + } + + for ( i = 255; i >= 0; i-- ) { + args2[i] = L' '; + } + argc2 = 0; + + TT_CompileDelta( dArr, deltaCount, (unsigned char) InstructionCode, args, &argc ); + if (CurrentPtr > SelStartPtr && *BinaryOffset == -1) + { + *BinaryOffset = (short)(ptrdiff_t)(iPtr - insStore); + } + InstructionCode = InstructionCode & 0xFF; /* for the diret delta */ + + if (iPtr + 1 > InnerBinaryOutMaxPtr) + { + *tt_error = tt_ProgramTooBig; + return CurrentPtr; + } + if ((char*)aPtr + argc + argc2 > (char*)InnerArgStoreMaxPtr) + { + *tt_error = tt_TooManyArguments; + return CurrentPtr; + } + TT_StoreArgumentsAndInstruction((unsigned char) InstructionCode, &aPtr, &iPtr, argc, args, argc2, args2, tt_error); + + } else if (InstructionCode == FakeCode) { + /* this is a composite information */ + *tt_error = tt_CompositeCode; + *SelectionLength = LineLength; + } else { + /* this is a regular instruction */ + wchar_t * LabelPtr, *BWLabelPtr; + short LabelLength, BWLabelLength; + wchar_t * EOLPtr, *LineBegin; + LineBegin = CurrentPtr; + EOLPtr = CurrentPtr + LineLength; + CurrentPtr = TT_ReadInstructionBooleans (CurrentPtr+StringLength, EOLPtr, InstructionIndex, &InstructionCode, SelectionLength, tt_error); + + if (TT_IsPushBW(InstructionCode) && CompilationStatus->WeAreInPushOnMode && *tt_error == tt_NoError) + { + *tt_error = tt_PUSHBWInPushON; + *SelectionLength = LineLength; + return LineBegin; + } + if ( *tt_error != tt_NoError) + { + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; + }; + + /* expand the argument list for a command affected by the SLOOP */ + if ( TT_ExpandArgs( tt_instruction[InstructionIndex].pops, argTypeBuffer, &loop, tt_error ) ) { + loop = 1; + } + if ( *tt_error != tt_NoError) + { + /* error should be SLOOP argument too big */ + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + *SelectionLength = SLoopLineLength; + return SLoopPtr; + }; + + CurrentPtr = TT_ReadInstructionParameters (CurrentPtr, EOLPtr, InstructionIndex, (unsigned char) InstructionCode, asm_ppDescription1, CompilationStatus->WeAreInPushOnMode, + argTypeBuffer, &argc, args, &argc2, args2, &LabelPtr, &LabelLength, &BWLabelPtr, &BWLabelLength, + SelectionLength, MaxFunctionDefs,BinaryOffset, flashingPoints,tt_error); + if ( *tt_error != tt_NoError) + { + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; + }; + + if ( InstructionCode == asm_SLOOP ) { + /* OK we got the arguments */ + if ( CompilationStatus->WeAreInPushOnMode ) { + SLoopLineLength = LineLength; + SLoopPtr = LineBegin; + loop = args[0]; + } else { + loop = 1; + } + } + + if (TT_IsJR(InstructionCode)) { + numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ + numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); /* number of instructions */ + + if (LabelLength != 0 || BWLabelLength != 0) + { + NeedTwoPass = true; /* we have a label to replace by the real value in the second pass */ + + CurrentPtr = TT_SaveJR( numberofArgs+argc /* we need to count for the boolean if given as an argument, hence the + argc */ + , numberofInstructions, CurrentPtr, LabelPtr, LabelLength, BWLabelPtr, BWLabelLength, JRList, JrBW, argStore, SelectionLength , tt_error); + } + }; + + + if (InstructionCode == 0x8e /* INSTCTRL */ && asmType != asmPREP) + { + *tt_error = tt_INSTCTRLnotInPreProgram; + *SelectionLength = LineLength; + return LineBegin; + } + + if (TT_IsIDEF_FDEFInstruction(InstructionCode) && asmType == asmGLYF) + { + *tt_error = tt_IDEF_FDEFinGlyphProgram; + *SelectionLength = LineLength; + return LineBegin; + } + + if (CurrentPtr > SelStartPtr && *BinaryOffset == -1) + { + *BinaryOffset = (short)(ptrdiff_t)(iPtr - insStore); + } + + if (TT_IsPushBW(InstructionCode)) + { + if (iPtr + 1 + 2 * argc > InnerBinaryOutMaxPtr) + { + *tt_error = tt_ProgramTooBig; + return CurrentPtr; + } + } else + { + if (iPtr + 1 > InnerBinaryOutMaxPtr) + { + *tt_error = tt_ProgramTooBig; + return CurrentPtr; + } + if ((char*)aPtr + argc + argc2 > (char*)InnerArgStoreMaxPtr) + { + *tt_error = tt_TooManyArguments; + return CurrentPtr; + } + } + + TT_StoreArgumentsAndInstruction((unsigned char)InstructionCode, &aPtr, &iPtr, argc, args, argc2, args2, tt_error); + } + } + + } + } + + + + } + + if (*tt_error != tt_NoError) + { + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; + }; + + if (RecursionLevel > 1) + { + *tt_error = tt_MissingEndBlock; + } else { + /* write the new code */ + numberofArgs = (short)(ptrdiff_t)(aPtr - argStore); /* number of arguments */ + numberofInstructions = (short)(ptrdiff_t)(iPtr - insStore); /* number of instructions */ + + if (NeedTwoPass) + { + CurrentPtr = TT_FindLabelError(PushLabels, JrBW, JRList, Label, CurrentPtr, SelectionLength, tt_error) ; + + if (*tt_error != tt_NoError) + { + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; + }; + TT_JRpushON_ReplaceLabel( JRList, Label, argStore, tt_error); + TT_JRpushOFF_ReplaceLabel( JrBW, PushLabels, Label, tt_error); + } + + if (*tt_error == tt_NoError) + *BinaryLength = TT_WriteOutBlock( BinaryOut, (unsigned char *)BinaryOutEndPtr, BinaryOffset, AddOffset, argStore, insStore, numberofArgs, numberofInstructions, tt_error); + *StackNeed = *StackNeed + numberofArgs; + } + + DisposeP((void**)&argStore); + DisposeP((void**)&insStore); + TT_FreeAllLabelMemory(PushLabels, JrBW, Label, JRList); + return CurrentPtr; +} + + +wchar_t *TT_Compile( + /* source text, pointer to the begining, the end and the cursor position (to be able to trace until that line) */ + wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, + /* pointer to the output buffer, it's maximal length and return the length of the resulting binary */ + unsigned char * BinaryOut, short MaxBinaryLength, int32_t * BinaryLength, + /* offset in the instruction stream to the instruction corresponding to the cursor position, used for trace mode */ + short * BinaryOffset, + /* length of the text to be selected in case of error */ + int32_t * SelectionLength, + /* line number where the first error occur */ + short * ErrorLineNb, + /* return : approximate stack need, higher function number */ + short * StackNeed, short * MaxFunctionDefs, + + /* for the DovMan partial compilation feature that flash points referenced by the current command */ + tt_flashingPoints * flashingPoints, + + ASMType asmType, /* asmGLYF, asmPREP, or asmFPGM */ + /* error code, return tt_NoError if no error */ + short * tt_error); + +wchar_t *TT_Compile(wchar_t *StartPtr, wchar_t * EndPtr, wchar_t * SelStartPtr, unsigned char * BinaryOut, int32_t MaxBinaryLength, + int32_t * BinaryLength, short * BinaryOffset, int32_t * SelectionLength, short * ErrorLineNb, + short * StackNeed, short * MaxFunctionDefs, + /* for the DovMan partial compilation feature that flash points referenced by the current command */ + tt_flashingPoints * flashingPoints, + ASMType asmType, short * tt_error) +{ + tt_CompilationStatus CompilationStatus; + wchar_t * Result; + char * BinaryOutEndPtr; + *ErrorLineNb = 0; + + BinaryOutEndPtr = (char *) (BinaryOut + MaxBinaryLength); + + CompilationStatus.WeAreInsideAnIF = false; + CompilationStatus.NumberOfEmbeddedIF = 0; + CompilationStatus.WeAreInsideAnFDEF = false; + CompilationStatus.WeAreInsideAnIDEF = false; + CompilationStatus.WeAreInPushOnMode = true; + CompilationStatus.WeAreInPrePushMode = true; + CompilationStatus.WeAreInsideGHBlock = false; + CompilationStatus.LastCommandWasAnIF = false; + CompilationStatus.LastCommandWasAnELSE = false; + CompilationStatus.LastCommandWasAnFDEF = false; + CompilationStatus.LastCommandWasAnIDEF = false; + CompilationStatus.LastCommandWasAnJUMP = false; + CompilationStatus.LastCommandWasAnEND = false; + + flashingPoints->NumberOfFlashingPoint = 0; + + Result = TT_InnerCompile(StartPtr, EndPtr, SelStartPtr, BinaryOut, BinaryOutEndPtr, + BinaryLength, BinaryOffset, SelectionLength, ErrorLineNb, + StackNeed, MaxFunctionDefs, 1, &CompilationStatus, flashingPoints, asmType, tt_error); + + if (*tt_error != tt_NoError) return Result; /* avoid to rewrite the error code by a junk code */ + + if (CompilationStatus.NumberOfEmbeddedIF != 0) + { + *tt_error = tt_IFwithoutEIF; + } + if (CompilationStatus.WeAreInsideAnFDEF) + { + *tt_error = tt_FDEFwithoutENDF; + } + if (CompilationStatus.WeAreInsideAnIDEF) + { + *tt_error = tt_IDEFwithoutENDF; + } + + return Result; +} + +void TT_GetErrorString (short ErrorNb, wchar_t * ErrorString, size_t errorStringLen); +void TT_GetErrorString (short ErrorNb, wchar_t * ErrorString, size_t errorStringLen) +{ + + switch (ErrorNb ) { + case tt_NoError: + swprintf(ErrorString, errorStringLen, L"There is no Error"); + break; + case tt_EmbeddedComment: + swprintf(ErrorString, errorStringLen, L"Nested comment"); + break; + case tt_UnterminatedComment: + swprintf(ErrorString, errorStringLen, L"Unterminated comment"); + break; + case tt_UnknownSwitch: + swprintf(ErrorString, errorStringLen, L"Unknown compiler switch"); + break; + case tt_UnknownInstruction: + swprintf(ErrorString, errorStringLen, L"Unknown instruction"); + break; + case tt_TwoInstructionsInSameLine: + swprintf(ErrorString, errorStringLen, L"End of line expected"); + break; + case tt_BooleanFlagsMissing: + swprintf(ErrorString, errorStringLen, L"bool flags missing"); + break; + case tt_WrongNumberOfBoolean: + swprintf(ErrorString, errorStringLen, L"Wrong number of boolean flags"); + break; + case tt_TooManyBooleans: + swprintf(ErrorString, errorStringLen, L"Too many booleans"); + break; + case tt_UnrecognizedBoolean: + swprintf(ErrorString, errorStringLen, L"Unrecognized boolean flag"); + break; + case tt_MissingClosingBracket: + swprintf(ErrorString, errorStringLen, L"Missing closing bracket"); + break; + case tt_SLOOPArgumentBufferTooSmall: + swprintf(ErrorString, errorStringLen, L"SLOOP number too big, the compiler cannot handle such a big number"); + break; + case tt_EmptyParameterList: + swprintf(ErrorString, errorStringLen, L"Missing comma between parameters or empty parameter list"); + break; + case tt_UnableToParseArgument: + swprintf(ErrorString, errorStringLen, L"Unable to parse argument"); + break; + case tt_MissingParameters: + swprintf(ErrorString, errorStringLen, L"Missing parameters or missing comma between parameters "); + break; + case tt_PointNbOutOfRange: + swprintf(ErrorString, errorStringLen, L"Point number out of range"); + break; + case tt_CVTIndexOutOfRange: + swprintf(ErrorString, errorStringLen, L"CVT index out of range"); + break; + case tt_StorageIndexOutOfRange: + swprintf(ErrorString, errorStringLen, L"Storage number out of range"); + break; + case tt_ContourNbOutOfRange: + swprintf(ErrorString, errorStringLen, L"Contour number out of range"); + break; + case tt_FunctionNbOutOfRange: + swprintf(ErrorString, errorStringLen, L"Function number out of range"); + break; + case tt_ArgumentOutOfRange: + swprintf(ErrorString, errorStringLen, L"Argument number out of range"); + break; + case tt_ArgumentIndexOutOfRange: + swprintf(ErrorString, errorStringLen, L"Compiler Error! Argument index out of range"); + break; + case tt_NotEnoughMemory: + swprintf(ErrorString, errorStringLen, L"Not enough memory"); + break; + case tt_DeltaListMissing: + swprintf(ErrorString, errorStringLen, L"Delta, parameter list missing"); + break; + case tt_DeltaOpeningParenthesisMissing: + swprintf(ErrorString, errorStringLen, L"Delta, opening parenthesis missing"); + break; + case tt_DeltaClosingParenthesisMissing: + swprintf(ErrorString, errorStringLen, L"Delta, closing parenthesis missing"); + break; + case tt_PointSizeOutOfRange: + swprintf(ErrorString, errorStringLen, L"Delta, point size out of range"); + break; + case tt_DeltaDenominatorMissing: + swprintf(ErrorString, errorStringLen, L"Delta, denominator missing, format should be eg: (19 @12 3/8)"); + break; + case tt_DeltaWrongDenominator: + swprintf(ErrorString, errorStringLen, L"Delta, wrong denominator, format should be eg: (19 @12 3/8)"); + break; + case tt_DeltaAtSignMissing: + swprintf(ErrorString, errorStringLen, L"Delta, @ sign missing, format should be eg: (19 @12 3/8)"); + break; + case tt_DeltaClosingBracketMissing: + swprintf(ErrorString, errorStringLen, L"Delta, closing bracket missing"); + break; + case tt_TooManyDeltas: + swprintf(ErrorString, errorStringLen, L"Delta, too many deltas in the same line"); + break; + case tt_DeltaOORangePpem: + swprintf(ErrorString, errorStringLen, L"Delta, out of range ppem for Delta"); + break; + case tt_TooManyLabels: + swprintf(ErrorString, errorStringLen, L"Too many labels in the same block"); + break; + case tt_LabelTooLong: + swprintf(ErrorString, errorStringLen, L"Label too long, limited to %hd character", (short)(MAXLABELLENGTH - 1)); + break; + case tt_DuplicateLabel: + swprintf(ErrorString, errorStringLen, L"Same label used twice"); + break; + case tt_EndWithoutBegin: + swprintf(ErrorString, errorStringLen, L"#END without corresponding #BEGIN"); + break; + case tt_MissingEndBlock: + swprintf(ErrorString, errorStringLen, L"End(s) of block, #END, missing"); + break; + case tt_TooManyEnbeddedBlocks: + swprintf(ErrorString, errorStringLen, L"Too many levels of nested blocks, limit = %hd", (short)(MAXBLOCKEMBEDDING - 1)); + break; + case tt_CompositeCode: + swprintf(ErrorString, errorStringLen, L"Composite commands mixed into TrueType code"); + break; + case tt_VoidLabel: + swprintf(ErrorString, errorStringLen, L"NULL label, must have at least one character"); + break; + case tt_LabelNotFound: + swprintf(ErrorString, errorStringLen, L"Corresponding label not found"); + break; + case tt_ExpectingAComma: + swprintf(ErrorString, errorStringLen, L"#PUSH argument list, missing arguments or missing comma"); + break; + case tt_TooManyPushArgs: + swprintf(ErrorString, errorStringLen, L"#PUSH, too many arguments, limit = %hd", (short)(PUSH_ARG)); + break; + case tt_ParseOverflow: + swprintf(ErrorString, errorStringLen, L"Number too large to be parsed, larger than 32,767"); + break; + case tt_JRExpectingABracket: + swprintf(ErrorString, errorStringLen, L"JR instruction in PushOff mode, expecting an opening bracket"); + break; + case tt_JRExpectingABWLabel: + swprintf(ErrorString, errorStringLen, L"JR instruction in PushOff mode, expecting an Bn or a Wn label"); + break; + case tt_JRExpectingAEqual: + swprintf(ErrorString, errorStringLen, L"JR instruction in PushOff mode, expecting an equal between labels"); + break; + case tt_JRExpectingALabel: + swprintf(ErrorString, errorStringLen, L"JR instruction in PushOff mode, expecting a #label"); + break; + case tt_JumpTooBigForByte: + swprintf(ErrorString, errorStringLen, L"#PUSH, Bn : jump too far to be a byte"); + break; + case tt_JumpNegativeForByte: + swprintf(ErrorString, errorStringLen, L"#PUSH, Bn : negative jump cannot be a byte, use Wn"); + break; + case tt_EIFwithoutIF: + swprintf(ErrorString, errorStringLen, L"EIF without IF"); + break; + case tt_ELSEwithoutIF: + swprintf(ErrorString, errorStringLen, L"ELSE without IF"); + break; + case tt_ELSEwithinELSE: + swprintf(ErrorString, errorStringLen, L"expecting a EIF"); + break; + case tt_TooManyEmbeddedIF: + swprintf(ErrorString, errorStringLen, L"too many embedded IF"); + break; + case tt_ExpectingaBEGIN: + swprintf(ErrorString, errorStringLen, L"expecting a #BEGIN after IF[], ELSE[], FDEF[] or IDEF[] in push on mode"); + break; + case tt_FDEFInsideFDEF: + swprintf(ErrorString, errorStringLen, L"FDEF found within FDEF - ENDF pair"); + break; + case tt_FDEFInsideIDEF: + swprintf(ErrorString, errorStringLen, L"FDEF found within IDEF - ENDF pair"); + break; + case tt_IDEFInsideFDEF: + swprintf(ErrorString, errorStringLen, L"IDEF found within FDEF - ENDF pair"); + break; + case tt_IDEFInsideIDEF: + swprintf(ErrorString, errorStringLen, L"IDEF found within IDEF - ENDF pair"); + break; + case tt_ENDFwithoutFDEF: + swprintf(ErrorString, errorStringLen, L"ENDF found without corresponding FDEF or IDEF"); + break; + case tt_IFwithoutEIF: + swprintf(ErrorString, errorStringLen, L"IF without corresponding EIF"); + break; + case tt_FDEFwithoutENDF: + swprintf(ErrorString, errorStringLen, L"FDEF without corresponding ENDF"); + break; + case tt_IDEFwithoutENDF: + swprintf(ErrorString, errorStringLen, L"IDEF without corresponding ENDF"); + break; + + case tt_PUSHONwhenAlreadyOn: + swprintf(ErrorString, errorStringLen, L"#PUSHON when already in push on mode"); + break; + case tt_PUSHOFFwhenAlreadyOff: + swprintf(ErrorString, errorStringLen, L"#PUSHOFF when already in push off mode"); + break; + case tt_IFgoingAcrossBlocks: + swprintf(ErrorString, errorStringLen, L"IF statement going across block (#BEGIN #END) boundaries"); + break; + case tt_FDEFgoingAcrossBlocks: + swprintf(ErrorString, errorStringLen, L"FDEF statement going across block (#BEGIN #END) boundaries"); + break; + case tt_IDEFgoingAcrossBlocks: + swprintf(ErrorString, errorStringLen, L"IDEF statement going across block (#BEGIN #END) boundaries"); + break; + + case tt_IDEF_FDEFinGlyphProgram: + swprintf(ErrorString, errorStringLen, L"FDEF and IDEF can be called only from font program or the pre-program"); + break; + + case tt_INSTCTRLnotInPreProgram: + swprintf(ErrorString, errorStringLen, L"INSTCTRL[] can only be called from the pre-program"); + break; + case tt_ProgramTooBig: + swprintf(ErrorString, errorStringLen, L"Program too big, if you really need such a big program, call product support"); + break; + case tt_TooManyArguments: + swprintf(ErrorString, errorStringLen, L"Program too big (too many arguments), if you really need such a big program, call product support"); + break; + case tt_DELTAWithoutArguments: + swprintf(ErrorString, errorStringLen, L"DELTA without argument in PUSHON mode"); + break; + case tt_DELTAWithArguments: + swprintf(ErrorString, errorStringLen, L"DELTA with arguments in PUSHOFF mode"); + break; + case tt_PUSHBWInPushON: + swprintf(ErrorString, errorStringLen, L"Invalid use of PUSHB or PUSHW in PUSHON mode, use #PUSH instead"); + break; + case tt_WildCardInPush: + swprintf(ErrorString, errorStringLen, L"Invalid use * in a PUSH instruction"); + break; + + case tt_NotImplemented: + swprintf(ErrorString, errorStringLen, L"Not implemented"); + break; + + default : + swprintf(ErrorString, errorStringLen, L"Unknown error!"); + break; + } + +} + + +typedef struct { + const wchar_t *name; /* Apple instruction name */ + const wchar_t *description; /* descriptive info */ + const wchar_t *pops; /* What the instruction pops */ + const wchar_t *booleans; /* booleans */ +} co_InstructionType; + + +typedef struct { + wchar_t type; + wchar_t Reserved1; /* for struct alignment */ + wchar_t Reserved2; + wchar_t Reserved3; + const wchar_t *explanation; + short lowestValidValue; /* 6-7-90 JTS Range Checking Adds */ + short highestValidValue; /* 6-7-90 JTS Range Checking Adds */ +} co_ParameterDescriptionType; + +/* lower case is reserved for loop variable dependent pops */ +co_ParameterDescriptionType co_ppDescription[] = { + { L'G',L' ',L' ',L' ', L"Glyph Index" ,0 ,(short)USHORTMAX}, // allow full unicode range for glyph index B.St. + { L'O',L' ',L' ',L' ', L"Offset " ,SHORTMIN ,SHORTMAX}, + { L'P',L' ',L' ',L' ', L"Point Number" ,0 ,SHORTMAX}, + { L'M',L' ',L' ',L' ', L"Matrix elements, can be either integer or real numbers" ,0,0} +}; + +#define NumberOfParameterDescrEntries 4 + +const co_InstructionType co_instruction[] = { +/**** Instruction name, descriptive info, pops, booleans *****/ + { L"OFFSET", L"Component character", L"GOO", L"R" }, + { L"SOFFSET", L"Component character", L"GOOMMMM", L"R" }, + { L"ANCHOR", L"Component character", L"GPP", L"" }, + { L"SANCHOR", L"Component character", L"GPPMMMM", L"" }, + { L"OVERLAP", L"Component character", L"", L"" }, + { L"NONOVERLAP", L"Component character", L"", L"" }, + { L"USEMYMETRICS", L"Component character", L"", L"" }, + { L"SCALEDCOMPONENTOFFSET", L"Component character", L"", L"" }, + { L"UNSCALEDCOMPONENTOFFSET", L"Component character", L"", L"" } +}; + +typedef struct { + wchar_t type; + wchar_t code; + char result; +} co_BooleanTranslationType; + +const co_BooleanTranslationType co_booleanTranslation[] = { + { L'R', L'R', 1 }, + { L'R', L'r', 0 } +}; + +#define co_NumberOfBooleanTranslations (sizeof(co_booleanTranslation) / sizeof(co_BooleanTranslationType)) + + +#define CONUMBEROFINSTRUCTIONS (sizeof(co_instruction) / sizeof(co_InstructionType)) + + +wchar_t *CO_Parse2_14Number( wchar_t *p, wchar_t *endP,short *Number, short * error ); +wchar_t *CO_Parse2_14Number( wchar_t *p, wchar_t *endP,short *Number, short * error ) +{ + short i, negatif; + int32_t tempNumber; + + /* skip white space */ + while ( *p == L' ' && p < endP) + p++; + + tempNumber = 0; + negatif = false; + i = 0; + + if ( *p == L'-' && p < endP ) + { + negatif = true; + p++; + } + + if ( *p == L'+' && p < endP && !negatif) + /* we accept + or - but don't want -+ together */ + { + p++; + } + + /* read the first number */ + if ( *p >= L'0' && *p <= L'9' && p < endP ) + { + if ( *p > L'2') { + *error = tt_ParseOverflow; + return p; + } + tempNumber = tempNumber * 10 + (short) *p - (short) '0'; + p++; + i++; + } else { + *error = tt_UnableToParseArgument; + return p; + } + + + if ( *p >= L'0' && *p <= L'9' && p < endP ) + { + *error = tt_ParseOverflow; + return p; + } + + if ( *p == L'.' && p < endP ) + { + p++; + /* read the fractional part */ + while ( *p >= L'0' && *p <= L'9' && p < endP ) + { + if (i < 5) /* ignore the remainding digits */ + { + tempNumber = tempNumber * 10 + (short) *p - (short) '0'; + i++; + } + p++; + } + } + + while (i < 5) + { + tempNumber = tempNumber * 10; + i++; + } + + if (i == 0) *error = tt_UnableToParseArgument; + + + + tempNumber = tempNumber * 0x4000 / 10000; + if (negatif) tempNumber = - tempNumber; + + if (tempNumber > SHORTMAX) + { + tempNumber = SHORTMAX; + } + + if (tempNumber < SHORTMIN) + { + tempNumber = SHORTMIN; + } + + *Number = (short)tempNumber; + return p; + +} + +wchar_t * CO_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, + co_ParameterDescriptionType co_ppDescription[], short *argc,short *args, + int32_t * SelectionLength, short * co_error); +wchar_t * CO_ReadInstructionParameters (wchar_t * CurrentPtr, wchar_t * EOLPtr, short InstructionIndex, + co_ParameterDescriptionType co_ppDescription[], short *argc,short *args, + int32_t * SelectionLength, short * co_error) +{ + short argindex, argNb; + wchar_t * tempP; + // const wchar_t * argTypeBuffer = co_instruction[InstructionIndex].pops; + *argc = 0; + argindex = 0; + bool inRange; + + argNb = (short)STRLENW(co_instruction[InstructionIndex].pops); + + for (argindex=0;argindex= EOLPtr) break; + + if ( *CurrentPtr++ != L',' ) + { + *co_error = co_EmptyParameterList; + return (CurrentPtr-1); + } + + /* skip extra spaces */ + while ( *CurrentPtr == L' ' && CurrentPtr <= EOLPtr) + CurrentPtr++; + + if (CurrentPtr >= EOLPtr) break; + + if ( co_instruction[InstructionIndex].pops[argindex] == L'M' ) + { + /* there should be a 2.14 number */ + tempP = CurrentPtr; + CurrentPtr = CO_Parse2_14Number(CurrentPtr, EOLPtr, &args[argindex], co_error); + *argc = *argc + 1; + if (*co_error != co_NoError) return CurrentPtr; + } else { + short foundPP, PPindex; + + /* there should be a number */ + tempP = CurrentPtr; + CurrentPtr = TT_ParseNumber(CurrentPtr, EOLPtr, &args[argindex], SelectionLength, co_error); + if (*co_error != co_NoError) return CurrentPtr; + + foundPP = 0; /* Have not found it yet... */ + for (PPindex=0;(PPindex= EOLPtr) + { + *co_error = co_BooleanFlagsMissing; + *Selectionlength = 1; + return CurrentPtr; + }; + tempP = CurrentPtr; + CurrentPtr ++; + NBOfBooleans = (short)STRLENW(co_instruction[InstructionIndex].booleans) ; + booleanShift = NBOfBooleans; + + for (booleanCount = 0; + // for balance L'[' + booleanCount < NBOfBooleans && *CurrentPtr != L']' && CurrentPtr < EOLPtr ; + booleanCount++ ) + { + booleanShift--; + for ( found = k = 0; k < (short)co_NumberOfBooleanTranslations; k++ ) { + if ( co_booleanTranslation[k].type == co_instruction[InstructionIndex].booleans[booleanCount] && + (co_booleanTranslation[k].code) == *CurrentPtr ) + { + found = 1; + *RoundingCode = co_booleanTranslation[k].result; + } + } + if (!found) + { + *co_error = co_UnrecognizedBoolean; + *Selectionlength = 1; + return CurrentPtr; + } + + CurrentPtr++; + } + + if (booleanCount != NBOfBooleans) + { + *co_error = co_WrongNumberOfBoolean; + *Selectionlength = (short)(CurrentPtr-tempP)+1; + return tempP; + }; + + if ( (CurrentPtr >= EOLPtr) || (*CurrentPtr == L',' ) ) + { + *co_error = co_MissingClosingBracket; + *Selectionlength = (short)(CurrentPtr-tempP); + return tempP; + }; + + if ( /* [ for balance */ *CurrentPtr != L']' ) + { + *co_error = co_TooManyBooleans; + *Selectionlength = (short)(CurrentPtr-tempP)+1; + return tempP; + }; + CurrentPtr ++; + return CurrentPtr; +} + +/* + * returns true on error compile the composite information + */ + +wchar_t *CO_Compile(TrueTypeFont * font, TrueTypeGlyph * glyph, wchar_t *StartPtr, wchar_t * EndPtr, short *numCompositeContours, short *numCompositePoints, int32_t * SelectionLength, short * co_error); +wchar_t *CO_Compile(TrueTypeFont * font, TrueTypeGlyph * glyph, wchar_t *StartPtr, wchar_t * EndPtr, short *numCompositeContours, short *numCompositePoints, int32_t * SelectionLength, short * co_error) { +// B.St.'s DISCLAIMER: So far, I've barely done a minimum to make this hideous piece of code somewhat understandable. +// The way this appears to work is by looking at a pair of composite instructions (prevInstrIndex and currInstrIndex) +// in a window that is being slided along the code. This appears to be used to determine, whether such instructions +// as USEMYMETRICS or OVERLAP and NONOVERLAP (the latter two being obsolete, as far as I understand the TT manual), +// are properly followed by further composite instructions... + short LineNb,LastLineCompiled,RoundingCode = 0,currInstrIndex = 0,prevInstrIndex = 0,args[256],argc = 0; + bool currInstrIsCompInstr,prevInstrIsCompInstr; + int32_t LineLength,StringLength; + wchar_t *CurrentPtr,*EOLPtr; + TTCompositeProfile compositeProfile; + sfnt_glyphbbox Newbbox; // for composite glyph + + Newbbox.xmin = SHRT_MAX; + Newbbox.ymin = SHRT_MAX; + Newbbox.xmax = SHRT_MIN; + Newbbox.ymax = SHRT_MIN; + + *SelectionLength = 0; + *co_error = co_NoError; + prevInstrIsCompInstr = false; + + glyph->componentSize = 0; + compositeProfile.GlobalUSEMYMETRICS = 0; + compositeProfile.GlobalSCALEDCOMPONENTOFFSET = 0; + compositeProfile.GlobalUNSCALEDCOMPONENTOFFSET = 0; + compositeProfile.GlobalNON_OVERLAPPING = 1; + compositeProfile.GlobalMORE_COMPONENTS = 1; + compositeProfile.nextExitOffset = false; + + compositeProfile.numberOfCompositeContours = 0; + compositeProfile.numberOfCompositePoints = 0; + compositeProfile.numberOfCompositeElements = 0; + + compositeProfile.anchorPrevPoints = -1; + + CurrentPtr = StartPtr; + LastLineCompiled = 0; + + for ( LineNb = 1; CurrentPtr <= EndPtr && *co_error == co_NoError; ) { + CurrentPtr = TT_SkipCommentsAndEmptyLines( CurrentPtr,EndPtr, &LineNb, co_error ); + if (*co_error != co_NoError) { *SelectionLength = 2; goto failure; } + + if (CurrentPtr == EndPtr) break; + + LineLength = TT_GetLineLength(CurrentPtr, EndPtr); + + if (LineNb == LastLineCompiled) { *co_error = co_TwoInstructionsInSameLine; *SelectionLength = LineLength; goto failure; } + LastLineCompiled = LineNb; + + // regular instruction + StringLength = TT_GetStringLength (CurrentPtr, EndPtr); + currInstrIndex = 0; + while (currInstrIndex < (short)CONUMBEROFINSTRUCTIONS && !(StringLength == (int32_t)STRLENW(co_instruction[currInstrIndex].name) && wcsncmp(CurrentPtr, co_instruction[currInstrIndex].name, StringLength) == 0)) currInstrIndex++; + currInstrIsCompInstr = currInstrIndex < (short)CONUMBEROFINSTRUCTIONS; + + if (prevInstrIsCompInstr) { + font->UpdateCompositeProfile(glyph,&compositeProfile,currInstrIsCompInstr ? CO_CompInstrFollow : CO_StdInstrFollow, RoundingCode, prevInstrIndex, args, argc, &Newbbox, co_error); + } + + if (!currInstrIsCompInstr) goto success; // because it is assumed to be the first TrueType instruction + + EOLPtr = CurrentPtr + LineLength; + prevInstrIndex = currInstrIndex; + CurrentPtr = CO_ReadInstructionBooleans (CurrentPtr+StringLength, EOLPtr, currInstrIndex, &RoundingCode, SelectionLength, co_error); + if (*co_error != co_NoError) goto failure; + + CurrentPtr = CO_ReadInstructionParameters (CurrentPtr, EOLPtr, currInstrIndex, co_ppDescription, &argc, args, SelectionLength, co_error); + if (*co_error != co_NoError) goto failure; + + prevInstrIsCompInstr = true; + } + + if (prevInstrIsCompInstr) { + font->UpdateCompositeProfile(glyph,&compositeProfile,CurrentPtr >= EndPtr ? CO_NothingFollows : CO_StdInstrFollow, RoundingCode, currInstrIndex, args, argc,&Newbbox,co_error); + if (*co_error != co_NoError) goto failure; + } + +success: + if (Newbbox.xmin != SHRT_MAX) { // update the bounding box of a composite glyph + glyph->xmin = Newbbox.xmin; + glyph->ymin = Newbbox.ymin; + glyph->xmax = Newbbox.xmax; + glyph->ymax = Newbbox.ymax; + } + *numCompositeContours = compositeProfile.numberOfCompositeContours; + *numCompositePoints = compositeProfile.numberOfCompositePoints; + return CurrentPtr; + +failure: + glyph->componentSize = 0; + *numCompositeContours = 0; + *numCompositePoints = 0; + return CurrentPtr; +} // CO_Compile + + +void CO_GetErrorString (short ErrorNb, wchar_t * ErrorString, size_t errorStringLen); +void CO_GetErrorString (short ErrorNb, wchar_t * ErrorString, size_t errorStringLen) +{ + + switch (ErrorNb ) { + case co_NoError: + swprintf(ErrorString, errorStringLen, L"There is no Error"); + break; + case tt_EmbeddedComment: + swprintf(ErrorString, errorStringLen, L"Embedded comment"); + break; + case tt_UnterminatedComment: + swprintf(ErrorString, errorStringLen, L"Unterminated comment"); + break; + case co_TwoInstructionsInSameLine: + swprintf(ErrorString, errorStringLen, L"End of line expected"); + break; + case co_BooleanFlagsMissing: + swprintf(ErrorString, errorStringLen, L"bool flags missing"); + break; + case co_WrongNumberOfBoolean: + swprintf(ErrorString, errorStringLen, L"Wrong number of boolean flags"); + break; + case co_TooManyBooleans: + swprintf(ErrorString, errorStringLen, L"Too many booleans"); + break; + case co_UnrecognizedBoolean: + swprintf(ErrorString, errorStringLen, L"Unrecognized boolean flag"); + break; + case co_MissingClosingBracket: + swprintf(ErrorString, errorStringLen, L"Missing closing bracket"); + break; + case co_EmptyParameterList: + swprintf(ErrorString, errorStringLen, L"Missing comma between parameters or empty parameter list"); + break; + case co_MissingParameters: + swprintf(ErrorString, errorStringLen, L"Missing parameters"); + break; + case co_PointNbOutOfRange: + swprintf(ErrorString, errorStringLen, L"Point number out of range"); + break; + case co_GlyphIndexOutOfRange: + swprintf(ErrorString, errorStringLen, L"Glyph index out of range"); + break; + case co_ArgumentOutOfRange: + swprintf(ErrorString, errorStringLen, L"Argument out of range"); + break; + case tt_ParseOverflow: + swprintf(ErrorString, errorStringLen, L"Number too big to be parsed, bigger than MaxShort"); + break; + case co_AnchorArgExceedMax: + swprintf(ErrorString, errorStringLen, L"Anchor argument exceed maximum value"); + break; + case co_AnchorNothingAbove: + swprintf(ErrorString, errorStringLen, L"Composite, no instruction in the line above"); + break; + case co_2_14Overflow: + swprintf(ErrorString, errorStringLen, L"Composite, number too big for 2.14 float"); + break; + case co_ComponentSizeOverflow: + swprintf(ErrorString, errorStringLen, L"Composite, too many components"); + break; + + case co_OverlapLastInstruction: + swprintf(ErrorString, errorStringLen, L"Composite, OVERLAP cannot be the last composite command"); + break; + case co_NonOverlapLastInstruction: + swprintf(ErrorString, errorStringLen, L"Composite, NONOVERLAP cannot be the last composite command"); + break; + case co_UseMymetricsLastInstruction: + swprintf(ErrorString, errorStringLen, L"Composite, USEMYMETRICS cannot be the last composite command"); + break; + case co_ScaledComponentOffsetLastInstruction: + swprintf(ErrorString, errorStringLen, L"Composite, SCALEDCOMPONENTOFFSET cannot be the last composite command"); + break; + case co_UnscaledComponentOffsetLastInstruction: + swprintf(ErrorString, errorStringLen, L"Composite, UNSCALEDCOMPONENTOFFSET cannot be the last composite command"); + break; + case co_ScaledComponentOffsetAlreadySet: + swprintf(ErrorString, errorStringLen, L"Composite, UNSCALEDCOMPONENTOFFSET (Microsoft compatible) cannot be the used when SCALEDCOMPONENTOFFSET (Apple compatible) is alread used. Only one can be used."); + break; + case co_UnscaledComponentOffsetAlreadySet: + swprintf(ErrorString, errorStringLen, L"Composite, SCALEDCOMPONENTOFFSET (Apple compatible) cannot be the used when UNSCALEDCOMPONENTOFFSET (Microsoft compatible) is alread used. Only one can be used."); + break; + case co_ComponentChangeOnVariationFont: + swprintf(ErrorString, errorStringLen, L"Composite definition has changed or is not present"); + break; + + case co_NotImplemented: + swprintf(ErrorString, errorStringLen, L"Not implemented"); + break; + default : + swprintf(ErrorString, errorStringLen, L"Unknown error!"); + break; + } + +} + +bool DisassemComponent(TrueTypeGlyph *glyph, TextBuffer *src, wchar_t errMsg[], size_t errMsgLen) { + short i,flags,glyphIndex,arg1,arg2,xscale = 0,yscale = 0,scale01 = 0,scale10 = 0; + wchar_t c,buf[maxLineSize]; + + i = 0; + do { + flags = glyph->componentData[i++]; + flags = SWAPW(flags); + glyphIndex = glyph->componentData[i++]; + glyphIndex = SWAPW(glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) { + arg1 = glyph->componentData[i++]; + arg1 = SWAPW(arg1); + arg2 = glyph->componentData[i++]; + arg2 = SWAPW(arg2); + } else if (flags & ARGS_ARE_XY_VALUES ) { // signed bytes + signed char *sByte = (signed char *)&glyph->componentData[i]; + arg1 = sByte[0]; + arg2 = sByte[1]; + i++; + } else { // unsigned bytes, do we ever have those? + unsigned char *uByte = (unsigned char *)&glyph->componentData[i]; + arg1 = uByte[0]; + arg2 = uByte[1]; + i++; + } + + if (flags & WE_HAVE_A_TWO_BY_TWO) { + xscale = glyph->componentData[i++]; + xscale = (signed short)SWAPW(xscale); + scale01 = glyph->componentData[i++]; + scale01 = (signed short)SWAPW(scale01); + scale10 = glyph->componentData[i++]; + scale10 = (signed short)SWAPW(scale10); + yscale = glyph->componentData[i++]; + yscale = (signed short)SWAPW(yscale); + } else if (flags & WE_HAVE_A_SCALE) { + short word = glyph->componentData[i++]; + xscale = yscale = SWAPW(word); + scale01 = scale10 = 0; + flags = (flags & ~WE_HAVE_A_SCALE) | WE_HAVE_A_TWO_BY_TWO; + } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { + xscale = glyph->componentData[i++]; + xscale = (signed short)SWAPW(xscale); + yscale = glyph->componentData[i++]; + yscale = (signed short)SWAPW(yscale); + scale01 = scale10 = 0; + flags = (flags & ~WE_HAVE_AN_X_AND_Y_SCALE) | WE_HAVE_A_TWO_BY_TWO; + } + + if (i > glyph->componentSize) { + swprintf(errMsg,errMsgLen,L"DisassemComponent: Component data size %hd does not match parsed %hd size", glyph->componentSize,i); + return false; + } + + if (flags & USE_MY_METRICS) src->Append(L"USEMYMETRICS[]\r"); + else if (!(flags & NON_OVERLAPPING)) src->Append(L"OVERLAP[]\r"); + // If both UNSCALED_COMPONENT_OFFSET and SCALED_COMPONENT_OFFSET are set we will only emit the UNSCALEDCOMPONENTOFFSET for Microsoft compatibility. These are mutually exclusive flags. + if (flags & UNSCALED_COMPONENT_OFFSET) src->Append(L"UNSCALEDCOMPONENTOFFSET[]\r"); else if (flags & SCALED_COMPONENT_OFFSET) src->Append(L"SCALEDCOMPONENTOFFSET[]\r"); + c = flags & ROUND_XY_TO_GRID ? L'R' : L'r'; + + if (flags & WE_HAVE_A_TWO_BY_TWO) { + if (flags & ARGS_ARE_XY_VALUES) swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"SOFFSET[%c",c); else swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"SANCHOR["); + swprintf(&buf[STRLENW(buf)],sizeof(buf)/sizeof(wchar_t),L"], %hu, %hd, %hd, %.4f, %.4f, %.4f, %.4f\r", + glyphIndex,arg1,arg2, + (double)xscale /16384.0 + 0.000005, (double)scale01/16384.0 + 0.000005, + (double)scale10/16384.0 + 0.000005, (double)yscale /16384.0 + 0.000005); + } else { + if (flags & ARGS_ARE_XY_VALUES) swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"OFFSET[%c",c); else swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"ANCHOR["); + swprintf(&buf[STRLENW(buf)],sizeof(buf)/sizeof(wchar_t),L"], %hu, %hd, %hd\r",glyphIndex,arg1,arg2); + } + src->Append(buf); + } while (flags & MORE_COMPONENTS); + + return true; +} // DisassemComponent + +bool TTAssemble(ASMType asmType, TextBuffer* src, TrueTypeFont* font, TrueTypeGlyph* glyph, + int32_t maxBinLen, unsigned char* bin, int32_t* actBinLen, bool variationCompositeGuard, int32_t* errPos, int32_t* errLen, wchar_t errMsg[], size_t errMsgLen) { + + wchar_t* startPtr, * endPtr, * SelStartPtr, * tempPtr; + short BinaryOffset, CompileError = co_NoError, StackNeed, MaxFunctionDefs, ErrorLineNb, componentSize, numCompositeContours, numCompositePoints, maxContourNumber, maxPointNumber; + int32_t srcLen, highestCvtNum; + sfnt_maxProfileTable profile; + short componentData[MAXCOMPONENTSIZE]; + + tt_flashingPoints MyflashingPoints; + + profile = font->GetProfile(); + highestCvtNum = font->TheCvt()->HighestCvtNum(); + + *actBinLen = 0; *errPos = -1; *errLen = 0; + + if (asmType == asmGLYF && glyph->composite && src->TheLength() == 0 && !DisassemComponent(glyph, src, errMsg, errMsgLen)) return false; + + srcLen = src->TheLength(); + startPtr = (wchar_t*)NewP((srcLen + 1L) * sizeof(wchar_t)); // if lengths are zero, we are guaranteed a pointer if + 1L + + SelStartPtr = startPtr; + size_t len; + src->GetText(&len, startPtr); + tempPtr = startPtr; + endPtr = startPtr + srcLen; + + switch (asmType) { + case asmFPGM: + TT_SetRangeCheck(0, 0, profile.maxElements - 1, 0, (short)highestCvtNum, profile.maxStorage - 1); + break; + case asmPREP: + /* twilight point may be used in the pre-program for italic slope,... */ + TT_SetRangeCheck(0, profile.maxTwilightPoints, profile.maxElements - 1, profile.maxFunctionDefs - 1, (short)highestCvtNum, profile.maxStorage - 1); + break; + case asmGLYF: + /* compile first the composite information */ + componentSize = glyph->componentSize; + memcpy(componentData, glyph->componentData, glyph->componentSize * sizeof(short)); + tempPtr = CO_Compile(font, glyph, tempPtr, endPtr, &numCompositeContours, &numCompositePoints, errLen, &CompileError); + if (CompileError == co_NoError) + { + // If a variation font check to make sure new component size and data matches pre CO_Compile() state. + if (font->IsVariationFont() && variationCompositeGuard && (glyph->componentSize > 0 || componentSize > 0)) + { + const CheckCompositeResult result = CheckCompositeVariationCompatible(componentData, componentSize * sizeof(short), glyph->componentData, glyph->componentSize * sizeof(short)); + if (result == CheckCompositeResult::Fail) + { + // VTT should not change definition of glyphs in a variation font. + CompileError = co_ComponentChangeOnVariationFont; + // Revert + glyph->componentSize = componentSize; + memcpy(glyph->componentData, componentData, componentSize * sizeof(short)); + + } + else if (result == CheckCompositeResult::Tolerance) + { + // VTT should not change definition of glyphs in a variation font. + // Revert but no error + glyph->componentSize = componentSize; + memcpy(glyph->componentData, componentData, componentSize * sizeof(short)); + } + } + } + if (CompileError == co_NoError) { + //if (componentSize == 0 && glyph->componentSize > 0) { // we've just created a new composite + if(glyph->componentSize > 0) { + maxContourNumber = numCompositeContours; maxPointNumber = numCompositePoints - 1 + PHANTOMPOINTS; // hence pick up-to-date profile data for range ends + } + else { + maxContourNumber = (short)glyph->numContoursInGlyph - 1; maxPointNumber = glyph->endPoint[maxContourNumber] + PHANTOMPOINTS; // else glyph data should still be up-to-date + } + TT_SetRangeCheck(maxContourNumber, maxPointNumber, profile.maxElements - 1, profile.maxFunctionDefs - 1, (short)highestCvtNum, profile.maxStorage - 1); + } + else { + *errPos = (int32_t)(tempPtr - startPtr); + if (*errLen < 1) *errLen = 1; // it is easy to find a selection than a cursor <--- and difficult to get it right in the first place??? + CO_GetErrorString(CompileError, errMsg, errMsgLen); + } + break; + } + + if (CompileError == co_NoError) { + + StackNeed = 0; + MaxFunctionDefs = 0; + + tempPtr = TT_Compile(tempPtr, endPtr, SelStartPtr, bin, maxBinLen, actBinLen, + &BinaryOffset, errLen, &ErrorLineNb, &StackNeed, &MaxFunctionDefs, &MyflashingPoints, asmType, &CompileError); + + if (CompileError == tt_NoError) { + font->UpdateAssemblerProfile(asmType, MaxFunctionDefs + 1, StackNeed, (short)(*actBinLen)); + } + else { + *errPos = (int32_t)(tempPtr - startPtr); + if (*errLen < 1) *errLen = 1; // it is easy to find a selection than a cursor <--- and difficult to get it right in the first place??? + TT_GetErrorString(CompileError, errMsg, errMsgLen); + + *actBinLen = 0; + if (asmType == asmGLYF) glyph->componentSize = 0; + } + } + + font->UpdateGlyphProfile(glyph); + DisposeP((void**)&startPtr); + + return CompileError == tt_NoError; + +} diff --git a/src/TTAssembler.h b/src/TTAssembler.h index 5743206..5b65942 100644 --- a/src/TTAssembler.h +++ b/src/TTAssembler.h @@ -1,24 +1,24 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef TTAssembler_dot_h -#define TTAssembler_dot_h - -#define co_AnchorArgExceedMax 112 -#define co_AnchorNothingAbove 113 -#define co_ComponentSizeOverflow 115 -#define co_OverlapLastInstruction 116 -#define co_NonOverlapLastInstruction 117 -#define co_UseMymetricsLastInstruction 118 -#define co_ScaledComponentOffsetLastInstruction 119 -#define co_UnscaledComponentOffsetLastInstruction 120 -#define co_ScaledComponentOffsetAlreadySet 121 -#define co_UnscaledComponentOffsetAlreadySet 122 -#define co_ComponentChangeOnVariationFont 123 - -bool DisassemComponent(TrueTypeGlyph *glyph, TextBuffer *src, wchar_t errMsg[]); - -bool TTAssemble(ASMType asmType, TextBuffer* src, TrueTypeFont* font, TrueTypeGlyph* glyph, - int32_t maxBinLen, unsigned char* bin, int32_t* actBinLen, bool variationCompositeGuard, int32_t* errPos, int32_t* errLen, wchar_t errMsg[]); - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef TTAssembler_dot_h +#define TTAssembler_dot_h + +#define co_AnchorArgExceedMax 112 +#define co_AnchorNothingAbove 113 +#define co_ComponentSizeOverflow 115 +#define co_OverlapLastInstruction 116 +#define co_NonOverlapLastInstruction 117 +#define co_UseMymetricsLastInstruction 118 +#define co_ScaledComponentOffsetLastInstruction 119 +#define co_UnscaledComponentOffsetLastInstruction 120 +#define co_ScaledComponentOffsetAlreadySet 121 +#define co_UnscaledComponentOffsetAlreadySet 122 +#define co_ComponentChangeOnVariationFont 123 + +bool DisassemComponent(TrueTypeGlyph *glyph, TextBuffer *src, wchar_t errMsg[], size_t errMsgLen); + +bool TTAssemble(ASMType asmType, TextBuffer* src, TrueTypeFont* font, TrueTypeGlyph* glyph, + int32_t maxBinLen, unsigned char* bin, int32_t* actBinLen, bool variationCompositeGuard, int32_t* errPos, int32_t* errLen, wchar_t errMsg[], size_t errMsgLen); + #endif // GlyphStruct_dot_h \ No newline at end of file diff --git a/src/TTEngine.cpp b/src/TTEngine.cpp index a9e79a9..ce84c68 100644 --- a/src/TTEngine.cpp +++ b/src/TTEngine.cpp @@ -1,1518 +1,1579 @@ -/***** - - TTEngine.c - New TypeMan Talk Compiler - TrueType Engine - - Copyright (c) Microsoft Corporation. - Licensed under the MIT License. - -*****/ -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include // wcslen -#include // printf -#include "pch.h" -#include "CvtManager.h" // LinkColor -#include "TTEngine.h" - -#define MAX_HEIGHTS 37 - -#define minBufLen 1024L // should be > 0 and preferrably a power of 2 - -/***** THE FOLLOWING #defines SHOULD BE COORDINATED WITH AutomaticConstraintGenerator!!!!! *****/ - -#define cvtCutInStorage 3 - -#define setTTVtoXItalDirFn 9 -#define setTTVtoYItalDirFn 10 -#define setTTVtoXAdjItalDirFn 16 -#define setTTVtoYAdjItalDirFn 40 -#define oldDiagMirpFn 20 -#define deltaCvtPpemRangeFn 70 -#define deltaCvtSinglePpemFn 71 -#define deltaKnotPpemRangeFn 72 -#define deltaKnotSinglePpemFn 73 -#define colorDeltaCvtPpemRangeFn 74 -#define colorDeltaCvtSinglePpemFn 75 -#define colorDeltaKnotPpemRangeFn 76 -#define colorDeltaKnotSinglePpemFn 77 -#define offsetCvtFn (deltaCvtPpemRangeFn - deltaKnotPpemRangeFn) -#define threshold2UseDeltaFn 4 -#define defaultDeltaBase 9 // ppem -#define defaultDeltaShift 3 // 1/8 pixel -#define roundDownToGridBelowPpemFn 82 -#define aspectRatioFn 83 -#define greyScalingFn 84 -#define doubleHeightFn 85 -#define italicRiseRunFn 88 -#define singleWeightFn 31 - // 78 through 100 all used by AutomaticConstraintGenerator - // resActDistFn00 101 - // resActDistFn01 102 - // resActDistFn10 103 - // resActDistFn11 104 -#define resMIRPFn00 105 - // resMIRPFn01 106 - // resMIRPFn10 107 - // resMIRPFn11 108 - // symDistFn 109 - // ipMedianOffsetFn 110 - // ipMedianFn 111 - // resAdjStrokePhaseFn 112 - // deltaPhaseOptStrokePosFn 113 -#define resMIAPFn 114 -#define resIPMDAPFn 115 -#define resIPMIRPFn 116 -#define resIPDMIRPFn 117 - // reversePvFn 118 - // condAlignFn 119 -#define resDDMIRP0Fn 120 -#define resDDMIRP1Fn 121 -#define resDDMIRP2Fn 122 -#define resDDMIRP3Fn 123 - // whichSideOfEdgeFn 124 -#define resIPDDMIRPFn 125 -#define resIPDDMIRPGlue0Fn 126 -#define resIPDDMIRPGlue1Fn 127 -#define resIPDDMIRPGlue2Fn 128 -#define resIPDDMIRPGlue3Fn 129 - // epXtoBaseItalicFn 130 -#define resIIPDMIRPFn 131 - // regularizeLineFn 132 -#define iIUPLoopFn 133 -#define iIUPSimpleFn 134 -#define iIUPBoundaryFn 135 - - -DeltaColor DeltaColorOfByte(unsigned char byte) { - static DeltaColor deltaColorOfByte[0xE0] = { - /* 0 */ blackDelta, - /* 1 */ greyDelta, - /* 2 */ ctNatVerRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 6 */ ctComVerRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 10 */ ctNatHorRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 14 */ ctComHorRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 18 */ ctNatVerBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 22 */ ctComVerBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 26 */ ctNatHorBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 30 */ ctComHorBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - /* 66 */ ctNatVerRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 70 */ ctComVerRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 74 */ ctNatHorRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 78 */ ctComHorRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 82 */ ctNatVerBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 86 */ ctComVerBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 90 */ ctNatHorBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - /* 94 */ ctComHorBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - /* 130 */ ctNatVerRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 134 */ ctComVerRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 138 */ ctNatHorRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 142 */ ctComHorRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 146 */ ctNatVerBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 150 */ ctComVerBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 154 */ ctNatHorBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 158 */ ctComHorBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - invalidDelta, invalidDelta, invalidDelta, invalidDelta, - /* 194 */ ctNatVerRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 198 */ ctComVerRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 202 */ ctNatHorRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 206 */ ctComHorRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 210 */ ctNatVerBGRFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 214 */ ctComVerBGRFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 218 */ ctNatHorBGRFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, - /* 222 */ ctComHorBGRFAWYAADelta, invalidDelta - }; - - return 0 <= byte && byte < 0xE0 ? deltaColorOfByte[byte] : invalidDelta; -} // DeltaColorOfByte - -DeltaColor DeltaColorOfOptions(bool grayScale, bool clearType, bool clearTypeCompWidth, /* bool clearTypeVertRGB, */ bool clearTypeBGR, bool clearTypeFract, bool clearTypeYAA, bool clearTypeGray) { - unsigned char byte; - - // we'll assume that the combination of options is valid (the UI enforces this) - byte = 0; - if (grayScale) byte += 1; - if (clearType) byte += 2; - if (clearTypeCompWidth) byte += 4; -/* if (clearTypeVertRGB) byte += 8; */ - if (clearTypeBGR) byte += 0x10; - if (clearTypeFract) byte += 0x40; - if (clearTypeYAA) byte += 0x80; - return DeltaColorOfByte(byte); -} // DeltaColorOfOptions - -unsigned char ByteOfDeltaColor(DeltaColor color) { - static unsigned char byteOfDeltaColor[numDeltaColors] = { - 0x5f, 0, 1, - 2, 6, 10, 14, 18, 22, 26, 30, 66, 70, 74, 78, 82, 86, 90, 94, - 130, 134, 138, 142, 146, 150, 154, 158, 194, 198, 202, 206, 210, 214, 218, 222, - 0xdf }; - - return 0 <= color && color < numDeltaColors ? byteOfDeltaColor[color] : 0xdf /* ??? */; -} // ByteOfDeltaColor - -char *AllDeltaColorBytes(void) { - static char allBytes[0x100] = "0, 1, 2, 6, 10, 14, 18, 22, 26, 30, 66, 70, 74, 78, 82, 86, 90, 94, 130, 134, 138, 142, 146, 150, 154, 158, 194, 198, 202, 206, 210, 214, 218, or 222"; - - return &allBytes[0]; -} // AllDeltaColorBytes - -void TTEngine::Emit(const wchar_t text[]) { /* abstract */ } -void TTEngine::AssertFreeProjVector(TTVDirection dir) { /* abstract */ } -void TTEngine::AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot) { /* abstract */ } -void TTEngine::AssertFVonCA(bool y) { /* abstract */ } -void TTEngine::AssertPVonCA(bool y) { /* abstract */ } -void TTEngine::AssertFVonPV(void) { /* abstract */ } -TTVDirection TTEngine::FVDir(void) { /* abstract */ return xRomanDir; } -TTVDirection TTEngine::PVDir(void) { /* abstract */ return xRomanDir; } -void TTEngine::AssertRefPoint(short rp, short knot) { /* abstract */ } -void TTEngine::AssertRefPointPair(short rp0, short rp1, short knot0, short knot1) { /* abstract */ } -short TTEngine::AssertEitherRefPointOnKnot(short rp0, short rp1, short knot) { /* abstract */ return 0; } -short TTEngine::AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp) { /* abstract */ return 0; } -void TTEngine::AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]) { /* abstract */ } -void TTEngine::AssertAutoFlip(bool on) { /* abstract */ } -void TTEngine::AssertRounding(Rounding round) { /* abstract */ } -void TTEngine::AssertRoundingBelowPpem(Rounding round, short ppem) { /* abstract */ } -void TTEngine::AssertSuperRounding(short period, short phase, short thresHold) { /* abstract */ } -void TTEngine::RoundDownToGridBelowPpem(short ppem) { /* abstract */ } -void TTEngine::IfPpemBelow(short ppem) { /* abstract */ } -void TTEngine::Else(void) { /* abstract */ } -void TTEngine::End(bool invalidateRefPoints) { /* abstract */ } -void TTEngine::MDAP(bool round, short knot) { /* abstract */ } -void TTEngine::MIAP(bool round, short knot, short cvt) { /* abstract */ } -void TTEngine::MDRP(bool minDist, bool round, short color, short knot) { /* abstract */ } -void TTEngine::MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative) { /* abstract */ } -void TTEngine::DMIRP(short knot, short cvt, short pvFrom, short pvTo) { /* abstract */ } -void TTEngine::ALIGNRP(short knot) { /* abstract */ } -void TTEngine::IP(short knots, short knot[]) { /* abstract */ } -void TTEngine::SHP(short rp, short knots, short knot[]) { /* abstract */ } -void TTEngine::SHPIX(short knots, short knot[], F26Dot6 amount) { /* abstract */ } -void TTEngine::SLOOP(short count) { /* abstract */ } -void TTEngine::ISECT(short intersection, short line0start, short line0end, short line1start, short line1end) { /* abstract */ } -void TTEngine::IUP(bool y) { /* abstract */ } -void TTEngine::IPRange(bool y, short parent0, short parent1, short start, short end) { /* abstract */ } -void TTEngine::DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]) { /* abstract */ } -void TTEngine::CALL24(short leftCvt, short rightCvt) { /* abstract */ } -void TTEngine::CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1) { /* abstract */ } -void TTEngine::CALL64(short parent, short child, short cvt, bool half, bool flip) { /* abstract */ } -void TTEngine::CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip) { /* abstract */ } -void TTEngine::CALL678(bool back, short knot, short sameSide, short cvt, short storage) { /* abstract */ } -void TTEngine::CALL012345(short type, short knot0, short knot1, short knot2, short cvt) { /* abstract */ } -void TTEngine::CALL6(short knots, short knot[], short targetKnot) { /* abstract */ } -void TTEngine::CALL378(short type, short targetKnot) { /* abstract */ } -void TTEngine::CALL88(short riseCvt, short runCvt) { /* abstract */ } -void TTEngine::ResMIAP(short child, short cvt) { /* abstract */ } -void TTEngine::ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1) { /* abstract */ } -void TTEngine::ResMIRP(short parent, short child, short cvt, bool useMinDist) { /* abstract */ } -void TTEngine::ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1) { /* abstract */ } -void TTEngine::ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1) { /* abstract */ } -void TTEngine::ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { /* abstract */ } -void TTEngine::ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1) { /* abstract */ } -void TTEngine::ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { /* abstract */ } -void TTEngine::CALL(short actParams, short anyNum[], short functNum) { /* abstract */ } -void TTEngine::ASM(bool inLine, wchar_t text[]) { /* abstract */ } -void TTEngine::INSTCTRL(short fromPpem, short toPpem) { /* abstract */ } -void TTEngine::SCANCTRL(short ctrl) { /* abstract */ } -void TTEngine::SCANTYPE(short type) { /* abstract */ } -void TTEngine::SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]) { /* abstract */ } -void TTEngine::SetAspectRatioFlag(void) { /* abstract */ } -void TTEngine::SetGreyScalingFlag(void) { /* abstract */ } -void TTEngine::SetClearTypeCtrl(short ctrl) { /* abstract */ } -void TTEngine::CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum) { /* abstract */ } -void TTEngine::ResetPrepState(void) { /* abstract */ } -void TTEngine::SetFunctionNumberBias(short bias) { /* abstract */ } -short TTEngine::GetFunctionNumberBias(void) { return 0; /* abstract */ } -void TTEngine::InitTTEngine(bool legacyCompile, bool *memError) { /* abstract */ } -void TTEngine::TermTTEngine(TextBuffer *glyfText, bool *memError) { /* abstract */ } -TTEngine::TTEngine(void) { /* abstract */ } -TTEngine::~TTEngine(void) { /* abstract */ } - -class TTSourceEngine : public TTEngine { -public: - virtual void Emit(const wchar_t text[]); - virtual void AssertFreeProjVector(TTVDirection dir); - virtual void AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot); - virtual void AssertFVonCA(bool y); - virtual void AssertPVonCA(bool y); - virtual void AssertFVonPV(void); - virtual TTVDirection FVDir(void); - virtual TTVDirection PVDir(void); - virtual void AssertRefPoint(short rp, short knot); - virtual void AssertRefPointPair(short rp0, short rp1, short knot0, short knot1); // not necessarily in that order - virtual short AssertEitherRefPointOnKnot(short rp0, short rp1, short knot); // returns the ref point actually asserted - virtual short AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp); // returns the knot actually asserted - virtual void AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]); - virtual void AssertAutoFlip(bool on); - virtual void AssertRounding(Rounding round); - virtual void AssertRoundingBelowPpem(Rounding round, short ppem); - virtual void AssertSuperRounding(short period, short phase, short thresHold); - virtual void RoundDownToGridBelowPpem(short ppem); - virtual void IfPpemBelow(short ppem); - virtual void Else(void); - virtual void End(bool invalidateRefPoints); - virtual void MDAP(bool round, short knot); - virtual void MIAP(bool round, short knot, short cvt); - virtual void MDRP(bool minDist, bool round, short color, short knot); - virtual void MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative); - virtual void DMIRP(short knot, short cvt, short pvFrom, short pvTo); - virtual void ALIGNRP(short knot); - virtual void IP(short knots, short knot[]); - virtual void SHP(short rp, short knots, short knot[]); - virtual void SHPIX(short knots, short knot[], F26Dot6 amount); - virtual void SLOOP(short count); - virtual void ISECT(short intersection, short line0start, short line0end, short line1start, short line1end); - virtual void IUP(bool y); - virtual void IPRange(bool y, short parent0, short parent1, short start, short end); - virtual void DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]); - virtual void CALL24(short leftCvt, short rightCvt); - virtual void CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1); - virtual void CALL64(short parent, short child, short cvt, bool half, bool flip); // "special MIRP" for new italic strokes - virtual void CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip); - virtual void CALL678(bool back, short knot, short sameSide, short cvt, short storage); // new italic strokes: extrapolate knot to cvt height or back again - virtual void CALL012345(short type, short knot0, short knot1, short knot2, short cvt); - virtual void CALL6(short knots, short knot[], short targetKnot); - virtual void CALL378(short type, short targetKnot); - virtual void CALL(short actParams, short anyNum[], short functNum); - virtual void CALL88(short riseCvt, short runCvt); - virtual void ResMIAP(short child, short cvt); - virtual void ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1); - virtual void ResMIRP(short parent, short child, short cvt, bool useMinDist); - virtual void ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1); - virtual void ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1); - virtual void ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); - virtual void ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1); - virtual void ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); - virtual void ASM(bool inLine, wchar_t text[]); - virtual void INSTCTRL(short fromPpem, short toPpem); - virtual void SCANCTRL(short ctrl); - virtual void SCANTYPE(short type); - virtual void SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]); - virtual void SetAspectRatioFlag(void); - virtual void SetGreyScalingFlag(void); - virtual void SetClearTypeCtrl(short ctrl); - virtual void CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum); - virtual void ResetPrepState(void); - virtual void SetFunctionNumberBias(short bias); - virtual short GetFunctionNumberBias(void); - virtual void InitTTEngine(bool legacyCompile, bool *memError); - virtual void TermTTEngine(TextBuffer *glyfText, bool *memError); - TTSourceEngine(void); - virtual ~TTSourceEngine(void); -private: - void InitTTEngineState(bool forNewGlyph); // false => after Call or Inline - bool error; - short fnBias; - short rp[3]; // zp never changed - F26Dot6 minDist; - bool sRound; - Rounding round; - short period,phase,thresHold; - TTVectorDesc ttv[pv-fv+1],Ttv[pv-fv+1]; - bool usedpv,Usedpv; // I'm not sure whether this is always updated correctly upon reflecting all kinds of side-effects - bool autoFlip; - short deltaBase,deltaShift; - short lastChild; // to optimise the move flag in MDRP, MIRP - int32_t lastChildPos; // fixup position... - int32_t bufPos,bufLen; - wchar_t *buf; - wchar_t mov[2],min[2],rnd[2],col[numLinkColors][2]; // characters for encoding - bool legacyCompile; -}; - -void TTSourceEngine::Emit(const wchar_t text[]) { - int32_t len; - wchar_t *newBuf; - - if (this->error) return; // no further code generation possible... - len = (int32_t)STRLENW(text); - while (this->bufPos + len + 2 > this->bufLen) { // test for CR and trailing 0 - newBuf = (wchar_t*)NewP(2*this->bufLen * sizeof(wchar_t)); - this->error = newBuf == NULL; - if (this->error) return; // no further code generation possible... - memcpy(newBuf,this->buf,this->bufLen * sizeof(wchar_t)); - DisposeP((void**)&this->buf); - this->buf = newBuf; - this->bufLen = 2*this->bufLen; - } - memcpy(&this->buf[this->bufPos],text,len * sizeof(wchar_t)); - this->bufPos += len; - this->buf[this->bufPos++] = L'\x0D'; - this->buf[this->bufPos] = 0; // add trailing 0 -} // TTSourceEngine::Emit - -void TTSourceEngine::AssertFreeProjVector(TTVDirection dir) { - const TTVDirection fvDirMap[numTTVDirections] = {xRomanDir, yRomanDir, xRomanDir, yItalDir, xRomanDir, yAdjItalDir, diagDir, perpDiagDir}, - pvDirMap[numTTVDirections] = {xRomanDir, yRomanDir, xItalDir, yRomanDir, xAdjItalDir, yRomanDir, diagDir, perpDiagDir}; - wchar_t code[64]; - TTVDirection fvDir = fvDirMap[dir], - pvDir = pvDirMap[dir]; - - if (fvDir != this->ttv[fv].dir || pvDir != this->ttv[pv].dir) { - switch (dir) { - case xRomanDir: - swprintf(code,L"SVTCA[X]"); - break; - case yRomanDir: - swprintf(code,L"SVTCA[Y]"); - break; - case xItalDir: - swprintf(code,L"CALL[], %i",this->fnBias + setTTVtoXItalDirFn); - break; - case yItalDir: - swprintf(code,L"CALL[], %i",this->fnBias + setTTVtoYItalDirFn); - break; - case xAdjItalDir: - swprintf(code,L"CALL[], %i",this->fnBias + setTTVtoXAdjItalDirFn); - break; - case yAdjItalDir: - swprintf(code,L"CALL[], %i",this->fnBias + setTTVtoYAdjItalDirFn); - break; - default: - swprintf(code,L"/* invalid TT vector direction */"); - break; - } - this->Emit(code); - this->ttv[fv].dir = fvDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv].dir = pvDir; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; - this->usedpv = false; - } -} // TTSourceEngine::AssertFreeProjVector - -void TTSourceEngine::AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot) { - TTVDirection dir; - wchar_t buf[32]; - TTVectorDesc *v = &this->ttv[ttv == fv ? fv : pv], *proj; - - if (P0.x == P1.x && P0.y != P1.y) dir = (TTVDirection)((short)yRomanDir - (short)rot); // incidently vertical - else if (P0.y == P1.y && P0.x != P1.x) dir = (TTVDirection)((short)xRomanDir + (short)rot); // ... horizontal - else dir = (TTVDirection)((short)diagDir + (short)rot); - if (dir < diagDir) { - if (dir != v->dir) { - swprintf(buf,L"S%cVTCA[%c]",ttv == fv ? L'F' : L'P',L'X' + (dir == yRomanDir)); this->Emit(buf); - v->dir = dir; v->from = v->to = invalidKnotNum; - if (ttv > fv) this->usedpv = ttv == dpv; - } - } else { - if (!(((v->from == parent0 && v->to == parent1) || (v->from == parent1 && v->to == parent0)) && dir == v->dir && (ttv != dpv || this->usedpv))) { - - proj = &this->ttv[pv]; - if (ttv == fv && ((proj->from == parent0 && proj->to == parent1) || (proj->from == parent1 && proj->to == parent0)) && proj->dir == dir) { - swprintf(buf,L"SFVTPV[]"); - } else { - swprintf(buf,L"S" WIDE_STR_FORMAT L"VTL[%c], %hi, %hi",ttv == fv ? L"F" : (ttv == pv ? L"P" : L"DP"),dir == perpDiagDir ? L'R' : L'r',parent0,parent1); - } - this->Emit(buf); - v->dir = dir; v->from = parent0; v->to = parent1; - if (ttv > fv) this->usedpv = ttv == dpv; - } - } -} // TTSourceEngine::AssertTTVonLine - -void TTSourceEngine::AssertFVonCA(bool y) { - Vector P0 = {0, 0}, P1 = {0, 0}; - - if (y) P1.y = 1; else P1.x = 1; - this->AssertTTVonLine(fv,invalidKnotNum,invalidKnotNum,P0,P1,false); -} // TTSourceEngine::AssertFVonCA - -void TTSourceEngine::AssertPVonCA(bool y) { - Vector P0 = {0, 0}, P1 = {0, 0}; - - if (y) P1.y = 1; else P1.x = 1; - this->AssertTTVonLine(pv,invalidKnotNum,invalidKnotNum,P0,P1,false); -} // TTSourceEngine::AssertPVonCA - -void TTSourceEngine::AssertFVonPV() { - if (this->ttv[fv].dir != this->ttv[pv].dir || this->ttv[fv].from != this->ttv[pv].from || this->ttv[fv].to != this->ttv[pv].to) { - this->Emit(L"SFVTPV[]"); - this->ttv[fv] = this->ttv[pv]; - } -} // TTSourceEngine::AssertFVonPV - -TTVDirection TTSourceEngine::FVDir(void) { - return this->ttv[fv].dir; -} // TTSourceEngine::FVDir - -TTVDirection TTSourceEngine::PVDir(void) { - return this->ttv[pv].dir; -} // TTSourceEngine::PVDir - -void TTSourceEngine::AssertRefPoint(short rp, short knot) { - wchar_t buf[32]; - - if (this->rp[rp] != knot) { - if (rp == 0 && this->lastChild == knot) { // set reference point by turning on the "move" flag - this->buf[this->lastChildPos] = this->mov[true]; // fixup - } else { // set reference point by the respective TT code... - swprintf(buf,L"SRP%hi[], %hi",rp,knot); this->Emit(buf); - } - this->rp[rp] = knot; - } - if (rp == 0) this->lastChild = invalidKnotNum; -} // TTSourceEngine::AssertRefPoint - -void TTSourceEngine::AssertRefPointPair(short rp0, short rp1, short knot0, short knot1) { - if (this->rp[rp0] == knot1 || this->rp[rp1] == knot0) { - this->AssertRefPoint(rp0,knot1); - this->AssertRefPoint(rp1,knot0); - } else { // and if we have to set the reference points, might as well assert sequentially - this->AssertRefPoint(rp0,knot0); - this->AssertRefPoint(rp1,knot1); - } -} // TTSourceEngine::AssertRefPointPair - -short TTSourceEngine::AssertEitherRefPointOnKnot(short rp0, short rp1, short knot) { - if (this->rp[rp1] == knot) return rp1; - else { this->AssertRefPoint(rp0,knot); return rp0; } -} // TTSourceEngine::AssertEitherRefPointOnKnot - -short TTSourceEngine::AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp) { - if (this->rp[rp] == knot1) return knot1; - else { this->AssertRefPoint(rp,knot0); return knot0; } -} // TTSourceEngine::AssertEitherKnotOnRefPoint - -void TTSourceEngine::AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]) { - wchar_t buf[32]; - - switch (minDists) { - case 1: - if (this->minDist != pixelSize[0]) { - swprintf(buf,L"SMD[], %i",pixelSize[0]); this->Emit(buf); - this->minDist = pixelSize[0]; - } - break; - case 2: - this->Emit( L"MPPEM[]"); - if (this->minDist == pixelSize[1]) { - swprintf(buf,L"GT[], %hi, *",jumpPpemSize[1]); this->Emit(buf); // [TOS](= jumpPpemSize[1]) > [TOS-1](= MPPEM[]) ? - this->Emit( L"IF[], *"); // MPPEM[] < jumpPpemSize[1] ? - this->Emit( L"#BEGIN"); // but current minDist is pixelSize[1] - swprintf(buf,L"SMD[], %i",pixelSize[0]); this->Emit(buf); // hence use pixelSize[0] instead - } else if (this->minDist == pixelSize[0]) { - swprintf(buf,L"LTEQ[], %hi, *",jumpPpemSize[1]); this->Emit(buf); // [TOS](= jumpPpemSize[1]) ≤ [TOS-1](= MPPEM[]) ? - this->Emit( L"IF[], *"); // MPPEM[] ≥ jumpPpemSize[1] - this->Emit( L"#BEGIN"); // but current minDist is pixelSize[0] - swprintf(buf,L"SMD[], %i",pixelSize[1]); this->Emit(buf); // use pixelSize[1] instead - } else { - swprintf(buf,L"GT[], %hi, *",jumpPpemSize[1]); this->Emit(buf); - this->Emit( L"IF[], *"); - this->Emit( L"#BEGIN"); - swprintf(buf,L"SMD[], %i",pixelSize[0]); this->Emit(buf); - this->Emit( L"#END"); - this->Emit( L"ELSE[]"); - this->Emit( L"#BEGIN"); - swprintf(buf,L"SMD[], %i",pixelSize[1]); this->Emit(buf); - } - this->Emit( L"#END"); - this->Emit( L"EIF[]"); - this->minDist = -1; // undefined, because we don't know which branch was taken - break; - default: - break; - } -} // TTSourceEngine::AssertMinDist - -void TTSourceEngine::AssertAutoFlip(bool on) { - if (this->autoFlip != on) { - if (on) this->Emit(L"FLIPON[]"); else this->Emit(L"FLIPOFF[]"); - this->autoFlip = on; - } -} // TTSourceEngine::AssertAutoFlip - -void TTSourceEngine::AssertRounding(Rounding round) { - if (this->sRound || round != this->round) { - switch (round) { - case rthg: this->Emit(L"RTHG[]"); break; - case rtdg: this->Emit(L"RTDG[]"); break; - case rtg: this->Emit(L"RTG[]"); break; - case rdtg: this->Emit(L"RDTG[]"); break; - case rutg: this->Emit(L"RUTG[]"); break; - default: this->Emit(L"ROFF[]"); break; - } - this->sRound = false; this->round = round; - } -} // TTSourceEngine::AssertRounding - -void TTSourceEngine::AssertRoundingBelowPpem(Rounding round, short ppem) { - wchar_t buf[32]; - - if (round == roff || ppem < 0) - this->AssertRounding(round); - else { - this->Emit( L"MPPEM[]"); - swprintf(buf,L"GT[], *, %hi",ppem); this->Emit(buf); - this->Emit( L"IF[], *"); - this->Emit( L"#BEGIN"); - switch (round) { - case rthg: this->Emit(L"RTHG[]"); break; - case rtdg: this->Emit(L"RTDG[]"); break; - case rtg: this->Emit(L"RTG[]"); break; - case rdtg: this->Emit(L"RDTG[]"); break; - case rutg: this->Emit(L"RUTG[]"); break; - default: this->Emit(L"ROFF[]"); break; - } - this->Emit( L"#END"); - this->Emit( L"ELSE[]"); - this->Emit( L"#BEGIN"); - this->Emit( L"ROFF[]"); - this->Emit( L"#END"); - this->Emit( L"EIF[]"); - this->sRound = false; this->round = rnone; - } -} // TTSourceEngine::AssertRoundingBelowPpem - -void TTSourceEngine::AssertSuperRounding(short period, short phase, short thresHold) { - wchar_t buf[32]; - - if (!this->sRound || period != this->period || phase != this->phase || thresHold != this->thresHold) { - swprintf(buf,L"SROUND[], %hi",(period << 6) + (phase << 4) + (thresHold + 4)); this->Emit(buf); // CEILING_ROUND never used, use threshold == -4 instead... - this->sRound = true; this->period = period; this->phase = phase; this->thresHold = thresHold; - } -} // TTSourceEngine::AssertSuperRounding - -void TTSourceEngine::RoundDownToGridBelowPpem(short ppem) { - wchar_t buf[32]; - - if (ppem < 0) - this->AssertRounding(rdtg); - else { - swprintf(buf,L"CALL[], %hi, %hi",ppem,this->fnBias + roundDownToGridBelowPpemFn); this->Emit(buf); - this->sRound = false; this->round = rnone; - } -} // TTSourceEngine::RoundDownToGridBelowPpem - -void TTSourceEngine::IfPpemBelow(short ppem) { // this is not nestable, nor general enough to deal with the entire graphics state, but can grow later, as needed - wchar_t buf[32]; - - this->Emit( L"MPPEM[]"); - swprintf(buf,L"GT[], *, %hi",ppem); this->Emit(buf); - this->Emit( L"IF[], *"); - this->Emit( L"#BEGIN"); - this->Ttv[fv] = this->ttv[fv]; - this->Ttv[pv] = this->ttv[pv]; - this->Usedpv = this->usedpv; -} // TTSourceEngine::IfPpemBelow - -void TTSourceEngine::Else(void) { - this->Emit( L"#END"); - this->Emit( L"ELSE[]"); - this->Emit( L"#BEGIN"); - this->ttv[fv] = this->Ttv[fv]; - this->ttv[pv] = this->Ttv[pv]; - this->usedpv = this->Usedpv; -} // TTSourceEngine::Else - -void TTSourceEngine::End(bool invalidateRefPoints) { - this->Emit( L"#END"); - this->Emit( L"EIF[]"); - this->ttv[fv].dir = diagDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; // assume any kind of side-effect, cf. InitTTEngine(true) - this->ttv[pv] = this->ttv[fv]; - this->usedpv = false; // ? - if (invalidateRefPoints) this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; -} // TTSourceEngine::End - -void TTSourceEngine::MDAP(bool round, short knot) { - wchar_t buf[32]; - - swprintf(buf,L"MDAP[%c], %hi",this->rnd[round],knot); this->Emit(buf); - this->rp[0] = this->rp[1] = knot; this->lastChild = invalidKnotNum; -} // TTSourceEngine::MDAP - -void TTSourceEngine::MIAP(bool round, short knot, short cvt) { - wchar_t buf[32]; - - swprintf(buf,L"MIAP[%c], %hi, %hi",this->rnd[round],knot,cvt); this->Emit(buf); - this->rp[0] = this->rp[1] = knot; this->lastChild = invalidKnotNum; -} // TTSourceEngine::MIAP - -void TTSourceEngine::MDRP(bool minDist, bool round, short color, short knot) { - wchar_t buf[32]; - - this->lastChild = knot; - this->lastChildPos = this->bufPos + 5; // a bit of a hack, but just a small one... - swprintf(buf,L"MDRP[%c%c%c%c%c], %hi",this->mov[false],this->min[minDist],this->rnd[round],this->col[color][0],this->col[color][1],knot); this->Emit(buf); - this->rp[1] = this->rp[0]; this->rp[2] = knot; -} // TTSourceEngine::MDRP - -void TTSourceEngine::MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative) { - wchar_t buf[32]; - - if (negative) { - swprintf(buf,L"RCVT[], %hi",cvt); this->Emit(buf); - this->Emit( L"NEG[], *"); - cvt = SECONDTMPCVT; - swprintf(buf,L"WCVTP[], %hi, *",cvt); this->Emit(buf); - } - this->lastChild = knot; - this->lastChildPos = this->bufPos + 5; // a bit of a hack, but just a small one... - swprintf(buf,L"MIRP[%c%c%c%c%c], %hi, %hi",this->mov[false],this->min[minDist],this->rnd[round],this->col[color][0],this->col[color][1],knot,cvt); this->Emit(buf); - this->rp[1] = this->rp[0]; this->rp[2] = knot; -} // TTSourceEngine::MIRP - -void TTSourceEngine::DMIRP(short knot, short cvt, short pvFrom, short pvTo) { - wchar_t buf[64]; - - swprintf(buf,L"CALL[], %hd, %hd, %hd, %hd, %hi",knot,cvt,pvFrom,pvTo,this->fnBias + oldDiagMirpFn); this->Emit(buf); - this->rp[1] = this->rp[0]; this->rp[2] = knot; this->lastChild = invalidKnotNum; - this->round = rtg; - this->ttv[pv].dir = perpDiagDir; this->ttv[pv].from = pvFrom; this->ttv[pv].to = pvTo; - this->usedpv = true; -} // TTSourceEngine::DMIRP - -void TTSourceEngine::ALIGNRP(short knot) { - wchar_t buf[32]; - - swprintf(buf,L"ALIGNRP[], %hi",knot); this->Emit(buf); -} // TTSourceEngine::ALIGNRP - -void TTSourceEngine::IP(short knots, short knot[]) { - wchar_t buf[8*maxParams]; - short i; - - swprintf(buf,L"IP[]"); - for (i = 0; i < knots; i++) swprintf(&buf[STRLENW(buf)],L", %hi",knot[i]); - this->Emit(buf); -} // TTSourceEngine::IP - -void TTSourceEngine::SHP(short rp, short knots, short knot[]) { - wchar_t buf[8*maxParams]; - short i; - - swprintf(buf,L"SHP[%hi]",rp); - for (i = 0; i < knots; i++) swprintf(&buf[STRLENW(buf)],L", %hi",knot[i]); - this->Emit(buf); -} // TTSourceEngine::SHP - -void TTSourceEngine::SHPIX(short knots, short knot[], F26Dot6 amount) { - wchar_t buf[8*maxParams]; - short i; - - swprintf(buf,L"SHPIX[]"); - for (i = 0; i < knots; i++) swprintf(&buf[STRLENW(buf)],L", %hi",knot[i]); - swprintf(&buf[STRLENW(buf)],L", %i",amount); - this->Emit(buf); -} // TTSourceEngine::SHPIX - -void TTSourceEngine::SLOOP(short count) { - wchar_t buf[32]; - - swprintf(buf,L"SLOOP[], %hi",count); this->Emit(buf); -} // TTSourceEngine::SLOOP - -void TTSourceEngine::ISECT(short intersection, short line0start, short line0end, short line1start, short line1end) { - wchar_t buf[48]; - - swprintf(buf,L"ISECT[], %hi, %hi, %hi, %hi, %hi",intersection,line0start,line0end,line1start,line1end); this->Emit(buf); -} // TTSourceEngine::ISECT - -void TTSourceEngine::IUP(bool y) { - wchar_t buf[8]; - - swprintf(buf,L"IUP[%c]",L'X' + (y & 1)); this->Emit(buf); -} // TTSourceEngine::IUP - -void TTSourceEngine::IPRange(bool y, short parent0, short parent1, short start, short end) { - wchar_t buf[64]; - - if (parent0 <= parent1) { - swprintf(buf,L"CALL[], %hi, %hi, %hi",parent0,parent1,this->fnBias + iIUPSimpleFn); this->Emit(buf); - } else { - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi",parent0,end,start,parent1,this->fnBias + iIUPBoundaryFn); this->Emit(buf); - } -} // TTSourceEngine::IPRange - -/***** -void FifthLoop(short *ppem, bool ppemSize[]); -void FifthLoop(short *ppem, bool ppemSize[]) { - do (*ppem)++; while (*ppem < maxPpemSize && !ppemSize[*ppem]); // next ppem to do -} - -void TTSourceEngine::DLT(short knot, F26Dot6 amount, bool ppemSize[]) { - short amnt,sign,steps,maxShift,magnitude[3],shift[3],ppem,deltaSubBase,deltas; - char buf[128]; - -// with a maximum esolution of 1 and a minimum resolution of 1/64, we can delta in range -8..+8 in steps of 1/64. -// this means that upto 3 steps have to be performed: doing the entire pixels, doing the 1/8 pixels, and finally -// doing the 1/64. If we delta by, say, 33/32, this can be done in two steps, e.g. 4/4 and 1/32. Likewise, 65/64 -// can be done as 1/1 and 1/64 - - amnt = (short)Abs(amount); sign = Sgn(amount); steps = 0; maxShift = 6; -// old version -// while (amnt != 0) { -// while (maxShift > 0 && (amnt & 1) == 0) { amnt >>= 1; maxShift--; } -// if (amnt > 8) { -// magnitude[steps] = sign*(amnt & 7); amnt = amnt >> 3; -// } else { // allow 8/1 (or e.g. 8/8 in a stream of other deltas with delta shift = 3) -// magnitude[steps] = sign*amnt; amnt = 0; -// } -// shift[steps] = maxShift; maxShift = Max(0,maxShift-3); -// steps++; -// } -// end of old version - if ((amnt & (short)0xffff << 6-this->deltaShift) == amnt && amnt >> 6-this->deltaShift <= 8) { // amount is "addressable" by current delta shift - magnitude[steps] = sign*(amnt >> 6-this->deltaShift); - shift[steps] = this->deltaShift; - steps++; - } else if ((amnt & (short)0xffff << stdDeltaShift) == amnt && amnt >> stdDeltaShift <= 8) { // amount is "addressable" by standard delta shift - magnitude[steps] = sign*(amnt >> stdDeltaShift); - shift[steps] = stdDeltaShift; - steps++; - } else { - while (amnt > 0) { - while (amnt > 8 && (amnt & 1) == 0) { amnt >>= 1; maxShift--; } - if (amnt > 8) { - magnitude[steps] = sign*(amnt & 7); amnt = amnt >> 3; - } else { // allow 8/1 (or e.g. 8/8 in a stream of other deltas with delta shift = 3) - magnitude[steps] = sign*amnt; amnt = 0; - } - shift[steps] = maxShift; maxShift = Max(0,maxShift-3); - steps++; - } - } - while (steps > 0) { // while there are steps to do - steps--; - if (this->deltaShift != shift[steps]) { // resolution is not the right one for this step - this->deltaShift = shift[steps]; - swprintf(buf,L"SDS[], %hi",this->deltaShift); this->Emit(buf); - } - ppem = 0; - while (ppem < maxPpemSize && !ppemSize[ppem]) ppem++; - while (ppem < maxPpemSize) { // while there are ppems to do - if (ppem < this->deltaBase || this->deltaBase + 47 < ppem) { // ppem is outside range "addressable" by DLTP1, DLTP2, or DLTP3 - this->deltaBase = ppem; - swprintf(buf,L"SDB[], %hi",this->deltaBase); this->Emit(buf); - } - deltaSubBase = this->deltaBase; - while (deltaSubBase + 15 < ppem) deltaSubBase += 16; - while (ppem < maxPpemSize && deltaSubBase <= this->deltaBase + 47) { // while there are ppems to do and they are "addressable" by DLTP1, DLTP2, or DLTP3 - if (ppem <= deltaSubBase + 15) { // if there are ppems to do which are "addressable" by "current" DLTPi - swprintf(buf,L"DLTP%hi[",((deltaSubBase-this->deltaBase) >> 4) + 1); deltas = 0; - do { - if (deltas == 4) { this->Emit(buf); swprintf(buf,L" "); deltas = 0; } - swprintf(&buf[STRLENW(buf)],L"(%hi @%hi %hi)",knot,ppem-deltaSubBase,magnitude[steps]); deltas++; - - FifthLoop(&ppem,ppemSize); // Think C compiler 7.0 doesn't like a 5th loop with code optimizations turned on, 7.0.3 does, though... - - // do ppem++; while (ppem < maxPpemSize && !ppemSize[ppem]); // next ppem to do - - } while (ppem <= deltaSubBase + 15); // while there are ppems to do which are "addressable" by "current" DLTPi - swprintf(&buf[STRLENW(buf)],L"]"); - this->Emit(buf); - } - deltaSubBase += 16; - } - } - } -} // TTSourceEngine::DLT -*****/ - -void SplitPpemSize(short threshold, bool ppemSize[], short *singleSizes, short singleSize[], short *ranges, short rangeLow[], short rangeHigh[]); -void SplitPpemSize(short threshold, bool ppemSize[], short *singleSizes, short singleSize[], short *ranges, short rangeLow[], short rangeHigh[]) { - short ppem,low; - - *singleSizes = *ranges = ppem = 0; - while (ppem < maxPpemSize && !ppemSize[ppem]) ppem++; - while (ppem < maxPpemSize) { - low = ppem; - while (ppem < maxPpemSize && ppemSize[ppem]) ppem++; - if (ppem - low >= threshold) { - rangeLow[*ranges] = low; - rangeHigh[(*ranges)++] = ppem-1; - } else { - while (low < ppem) - singleSize[(*singleSizes)++] = low++; - } - while (ppem < maxPpemSize && !ppemSize[ppem]) ppem++; - } -} // SplitPpemSize - -void TTSourceEngine::DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]) { - short amnt,sign,magnitude,shift,size,deltaSubBase,deltas; - bool singleStep; - wchar_t charFn,buf[128]; - short singlePpemSizes,singlePpemSize[maxPpemSize],ppemRanges,ppemRangeLow[maxPpemSize],ppemRangeHigh[maxPpemSize],offsFn; - -// with a maximum resolution of 1 and a minimum resolution of 1/64, we can delta in range -8..+8 in steps of 1/64. -// this means that upto 3 steps have to be performed: doing the entire pixels, doing the 1/8 pixels, and finally -// doing the 1/64. If we delta by, say, 33/32, this can be done in two steps, e.g. 4/4 and 1/32. Likewise, 65/64 -// can be done as 1/1 and 1/64 - - amnt = (short)Abs(amount); sign = Sgn(amount); - if (this->deltaShift >= 0 && (amnt & (short)0xffff << (6-this->deltaShift)) == amnt && amnt >> (6-this->deltaShift) <= 8) { // amount is "addressable" by current delta shift - magnitude = sign*(amnt >> (6-this->deltaShift)); - shift = this->deltaShift; - singleStep = true; - } else if (this->deltaShift >= 0 && (amnt & (short)0xffff << stdDeltaShift) == amnt && amnt >> stdDeltaShift <= 8) { // amount is "addressable" by standard delta shift - magnitude = sign*(amnt >> stdDeltaShift); - shift = stdDeltaShift; - singleStep = true; - } else { // may need a new delta shift, if we get away with a single delta at all - shift = 6; - while (amnt > 8 && (amnt & 1) == 0) { amnt >>= 1; shift--; } - magnitude = sign*amnt; - singleStep = amnt <= 8; - } - -// if it takes 2 steps (or more) to do a delta, we need two SDS[] and two DLTPi using 12 bytes (or three of each using 18 bytes), -// as opposed to a function call using 6 to 9 bytes only (6 for positive amount, 9 for negative due to sign extension and prepush - SplitPpemSize(singleStep && color == alwaysDelta ? threshold2UseDeltaFn : 1,ppemSize,&singlePpemSizes,singlePpemSize,&ppemRanges,ppemRangeLow,ppemRangeHigh); - - if (ppemRanges > 0) { - offsFn = cvt ? offsetCvtFn : 0; - for (size = 0; size < ppemRanges; size++) { - if (ppemRangeLow[size] == ppemRangeHigh[size]) - if (color == alwaysDelta) - if (cvt) - swprintf(buf,L"CALL[], %i, %hi, %hi, %hi",amount,knot,ppemRangeLow[size],this->fnBias + deltaCvtSinglePpemFn); - else - swprintf(buf,L"CALL[], %hi, %i, %hi, %hi",knot,amount,ppemRangeLow[size],this->fnBias + deltaKnotSinglePpemFn); - else - if (cvt) - swprintf(buf,L"CALL[], %i, %hi, %hi, %hi, %hi",amount,knot,ppemRangeLow[size],ByteOfDeltaColor(color),this->fnBias + colorDeltaCvtSinglePpemFn); - else - swprintf(buf,L"CALL[], %hi, %i, %hi, %hi, %hi",knot,amount,ppemRangeLow[size],ByteOfDeltaColor(color),this->fnBias + colorDeltaKnotSinglePpemFn); - else - if (color == alwaysDelta) - if (cvt) - swprintf(buf,L"CALL[], %i, %hi, %hi, %hi, %hi",amount,knot,ppemRangeLow[size],ppemRangeHigh[size],this->fnBias + deltaCvtPpemRangeFn); - else - swprintf(buf,L"CALL[], %hi, %i, %hi, %hi, %hi",knot,amount,ppemRangeLow[size],ppemRangeHigh[size],this->fnBias + deltaKnotPpemRangeFn); - else - if (cvt) - swprintf(buf,L"CALL[], %i, %hi, %hi, %hi, %hi, %hi",amount,knot,ppemRangeLow[size],ppemRangeHigh[size],ByteOfDeltaColor(color),this->fnBias + colorDeltaCvtPpemRangeFn); - else - swprintf(buf,L"CALL[], %hi, %i, %hi, %hi, %hi, %hi",knot,amount,ppemRangeLow[size],ppemRangeHigh[size],ByteOfDeltaColor(color),this->fnBias + colorDeltaKnotPpemRangeFn); - this->Emit(buf); - } - } - if (singlePpemSizes > 0) { - charFn = cvt ? L'C' : L'P'; - if (this->deltaShift != shift) { // resolution is not the right one for this step - this->deltaShift = shift; - swprintf(buf,L"SDS[], %hi",this->deltaShift); this->Emit(buf); - } - size = 0; - while (size < singlePpemSizes) { // while there are ppems to do - if (singlePpemSize[size] < this->deltaBase || this->deltaBase + 47 < singlePpemSize[size]) { // ppem is outside range "addressable" by DLTP1, DLTP2, or DLTP3 - this->deltaBase = singlePpemSize[size]; - swprintf(buf,L"SDB[], %hi",this->deltaBase); this->Emit(buf); - } - deltaSubBase = this->deltaBase; - while (size < singlePpemSizes && deltaSubBase <= this->deltaBase + 47) { // while there are ppems to do and they are "addressable" by DLTP1, DLTP2, or DLTP3 - if (size < singlePpemSizes && singlePpemSize[size] <= deltaSubBase + 15) { // if there are ppems to do which are "addressable" by "current" DLTPi - swprintf(buf,L"DLT%c%hi[",charFn,((deltaSubBase-this->deltaBase) >> 4) + 1); deltas = 0; - do { - if (deltas == 4) { this->Emit(buf); swprintf(buf,L" "); deltas = 0; } - swprintf(&buf[STRLENW(buf)],L"(%hi @%hi %hi)",knot,singlePpemSize[size]-deltaSubBase,magnitude); deltas++; - size++; - } while (size < singlePpemSizes && singlePpemSize[size] <= deltaSubBase + 15); // while there are ppems to do which are "addressable" by "current" DLTPi - swprintf(&buf[STRLENW(buf)],L"]"); - this->Emit(buf); - } - deltaSubBase += 16; - } - } - } -} // TTSourceEngine::DLT - -void TTSourceEngine::CALL24(short leftCvt, short rightCvt) { - wchar_t buf[32]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi",leftCvt,rightCvt,this->fnBias + 24); this->Emit(buf); -} // TTSourceEngine::CALL24 - -void TTSourceEngine::CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1) { - wchar_t buf[64]; - - switch (type) { - case 34: - case 35: - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi",knot3,cvt3,cvt2,knot1,cvt1,this->fnBias + type); - break; - case 36: - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi",knot3,cvt3,knot2,cvt2,knot1,cvt1,this->fnBias + type); - break; - } - this->Emit(buf); - this->ttv[fv].dir = yRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv] = this->ttv[fv]; - this->rp[0] = knot1; this->lastChild = invalidKnotNum; - this->rp[1] = knot1; - this->rp[2] = knot3; -} // TTSourceEngine::CALL3456 - -void TTSourceEngine::CALL64(short parent, short child, short cvt, bool half, bool flip) { - wchar_t buf[64]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi",parent,child,cvt,half,flip,this->fnBias + 64); this->Emit(buf); - this->rp[0] = this->rp[1] = parent; this->lastChild = invalidKnotNum; - this->rp[2] = child; -} // TTSourceEngine::CALL64 - -void TTSourceEngine::CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip) { - wchar_t buf[64]; - TTVectorDesc *v; - - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi",knot0,knot1,knot2,knot3,cvt,storage,(short)xLinks,(short)flip,this->fnBias + 65 + (short)crissCrossLinks); this->Emit(buf); - this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; this->lastChild = invalidKnotNum; - v = &this->ttv[pv]; v->dir = diagDir; v->from = v->to = invalidKnotNum; // assume any kind of side-effect, cf. InitTTEngine(true) - this->ttv[fv] = this->ttv[pv]; -} // TTSourceEngine::CALL656 - -void TTSourceEngine::CALL678(bool back, short knot, short sameSide, short cvt, short storage) { - wchar_t buf[64]; - TTVectorDesc *v; - - if (back) swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi",knot,sameSide,storage,this->fnBias + 68); - else swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi",knot,sameSide,cvt,storage,this->fnBias + 67); - this->Emit(buf); - v = &this->ttv[pv]; v->dir = diagDir; v->from = v->to = invalidKnotNum; // assume any kind of side-effect, cf. InitTTEngine(true) - this->ttv[fv] = this->ttv[pv]; - this->ttv[pv].dir = yRomanDir; // no reference points changed -} // TTSourceEngine::CALL678 - -void TTSourceEngine::CALL012345(short type, short knot0, short knot1, short knot2, short cvt) { - wchar_t buf[64]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi",knot0,knot1,knot2,cvt,this->fnBias + type); this->Emit(buf); - // no side-effects -} // TTSourceEngine::CALL012345 - -void TTSourceEngine::CALL6(short knots, short knot[], short targetKnot) { - short i; - wchar_t buf[256]; - - if (knots > 1) { - swprintf(buf,L"LOOPCALL[]"); - for (i = 0; i < knots; i++) swprintf(&buf[STRLENW(buf)],L", %hi, %hi",knot[i],targetKnot); - swprintf(&buf[STRLENW(buf)],L", %hi, %hi",knots,this->fnBias + 6); - } else if (knots == 1) - swprintf(buf,L"CALL[], %hi, %hi, %hi",knot[0],targetKnot,this->fnBias + 6); - if (knots > 0) { - this->rp[0] = targetKnot; this->lastChild = invalidKnotNum; - this->ttv[fv].dir = yRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv] = this->ttv[fv]; - this->Emit(buf); - } -} // TTSourceEngine::CALL6 - -void TTSourceEngine::CALL378(short type, short targetKnot) { - wchar_t buf[32]; - - swprintf(buf,L"CALL[], %hi, %hi",targetKnot,this->fnBias + type); this->Emit(buf); - this->rp[0] = type == 37 ? targetKnot + 1 : targetKnot - 1; this->lastChild = invalidKnotNum; - this->ttv[fv].dir = yRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv] = this->ttv[fv]; -} // TTSourceEngine::CALL378 - -void TTSourceEngine::CALL(short actParams, short anyNum[], short functNum) { - wchar_t buf[8*maxParams]; - short i; - - swprintf(buf,L"CALL[]"); - for (i = 0; i < actParams; i++) swprintf(&buf[STRLENW(buf)],L", %hi",anyNum[i]); - swprintf(&buf[STRLENW(buf)],L", %hi",functNum); // here functNum is passed explicitly as a parameter, hence we expect caller to correctly bias function number - this->Emit(buf); - this->InitTTEngineState(false); -} // TTSourceEngine::CALL - -void TTSourceEngine::CALL88(short riseCvt, short runCvt) { - wchar_t buf[32]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi",riseCvt,runCvt,this->fnBias + italicRiseRunFn); - this->Emit(buf); - this->rp[0] = this->rp[1] = invalidKnotNum; this->lastChild = invalidKnotNum; - this->sRound = false; this->round = rtg; - this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv] = this->ttv[fv]; -} // TTSourceEngine::CALL88 - -void TTSourceEngine::ResMIAP(short child, short cvt) { - wchar_t buf[64]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi",child,cvt,this->fnBias + resMIAPFn); - this->Emit(buf); - - this->lastChild = invalidKnotNum; // MSIRP in fpgm, cannot patch code in fpgm... - this->rp[0] = this->rp[1] = this->rp[2] = child; // MSIRP[M] used in fpgm -} // TTSourceEngine::ResMIAP - -void TTSourceEngine::ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1) { - wchar_t buf[64]; - short dirFlag; - - dirFlag = ((short)postRoundFlag)*8 + (short)pvP; - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi",dirFlag,parent0,child,parent1,this->fnBias + resIPMDAPFn); - this->Emit(buf); - - // SO FAR, WILL NEED TO MAKE MORE GENERAL TO ACCOMODATE fvP and fvC - if (postRoundFlag) { // only allowed on ResXIPAnchor - this->ttv[fv].dir = xRomanDir; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; - this->ttv[fv] = this->ttv[pv]; - this->rp[1] = child; - } else { - this->ttv[pv].dir = pvP; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; - this->ttv[fv] = this->ttv[pv]; - this->rp[1] = parent0; - } - this->usedpv = false; - this->lastChild = invalidKnotNum; - this->rp[0] = this->rp[2] = child; - // parent1; -} // TTSourceEngine::ResIPMDAP - -void TTSourceEngine::ResMIRP(short parent, short child, short cvt, bool useMinDist) { - bool useCvt; - int32_t pos; - wchar_t buf[64]; - - useCvt = cvt != invalidCvtNum; - pos = swprintf(buf,L"CALL[], %hi, %hi",parent,child); - if (useCvt) pos += swprintf(&buf[pos],L", %hi",cvt); - pos += swprintf(&buf[pos],L", %hi",this->fnBias + resMIRPFn00 + 2*(int32_t)useCvt + (int32_t)useMinDist); - - this->Emit(buf); - - this->lastChild = invalidKnotNum; // MSIRP in fpgm, cannot patch code in fpgm... - this->rp[1] = parent; - this->rp[0] = this->rp[2] = child; // MSIRP[M] used in fpgm -} // TTSourceEngine::ResMIRP - -void TTSourceEngine::ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1) { - wchar_t buf[64]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi",pvGP,grandParent0,parent,child,cvt,grandParent1,strokeOptimizationFlag,this->fnBias + resIPMIRPFn); - this->Emit(buf); - - this->ttv[pv].dir = (TTVDirection)((int32_t)pvGP % 2); this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; - this->ttv[fv] = this->ttv[pv]; // SO FAR, WILL NEED TO MAKE MORE GENERAL TO ACCOMODATE fvP and fvC - this->usedpv = false; - this->lastChild = invalidKnotNum; - this->rp[0] = this->rp[1] = this->rp[2] = parent; // parent SRP0[]ed and MSIRP[m]ed last in emulate interpolation of median -} // TTSourceEngine::ResIPMIRP - -void TTSourceEngine::ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1) { - wchar_t buf[128]; - int32_t pos; - - // CAUTION: this scheme doesn't support setting the fv PERPENDICULAR to a line (so far it doesn't need to) - pos = swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, ",parent0,child0,parent1,child1,cvt0,cvt1); - if (fv0.dir <= yRomanDir && fv1.dir <= yRomanDir) // simple case - swprintf(&buf[pos],L"%hi, %hi, %hi",(short)fv0.dir,(short)fv1.dir,this->fnBias + resDDMIRP0Fn); - else if (fv0.dir <= yRomanDir) // fv1.type set to line - swprintf(&buf[pos],L"%hi, %hi, %hi, %hi",(short)fv0.dir,fv1.from,fv1.to,this->fnBias + resDDMIRP1Fn); - else if (fv1.dir <= yRomanDir) // fv0.type set to line - swprintf(&buf[pos],L"%hi, %hi, %hi, %hi",fv0.from,fv0.to,(short)fv1.dir,this->fnBias + resDDMIRP2Fn); - else // both fv0.type and fv1.type set to line - swprintf(&buf[pos],L"%hi, %hi, %hi, %hi, %hi",fv0.from,fv0.to,fv1.from,fv1.to,this->fnBias + resDDMIRP3Fn); - this->Emit(buf); - - this->lastChild = invalidKnotNum; - this->rp[0] = parent0; // this->rp[1] = this->rp[2] = invalidKnotNum; unchanged - this->ttv[fv] = fv1; - this->ttv[pv].dir = perpDiagDir; this->ttv[pv].from = parent0; this->ttv[pv].to = child1; - this->usedpv = false; -} // TTSourceEngine::ResDDMIRP - -void TTSourceEngine::ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { - wchar_t buf[128]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi",pvGP,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1,this->fnBias + resIPDMIRPFn); - this->Emit(buf); - - this->ttv[pv].dir = (TTVDirection)((int32_t)pvGP % 2); this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; - this->ttv[fv] = this->ttv[pv]; // so far; may have to make more general to accomodate generalized freedom vectors used by the last knot moved, or have fn reset state to pvGP - this->usedpv = false; - this->lastChild = invalidKnotNum; - this->rp[0] = this->rp[1] = this->rp[2] = parent1; // parent1 SRP0[]ed and MSIRP[m]ed last in symDistFn -} // TTSourceEngine::ResIPDMIRP - -void TTSourceEngine::ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1) { - wchar_t buf[128]; - int32_t pos; - - // CAUTION: this scheme doesn't support setting the fv PERPENDICULAR to a line (so far it doesn't need to) - pos = swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, ",pvGP,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1); - if (fv0.dir < diagDir && fv1.dir < diagDir) // simple case - swprintf(&buf[pos],L"%hi, %hi, %hi",(int32_t)fv0.dir,(int32_t)fv1.dir,this->fnBias + resIPDDMIRPGlue0Fn); - else if (fv1.dir < diagDir) // fv0.type set to line - swprintf(&buf[pos],L"%hi, %hi, %hi, %hi",fv0.from,fv0.to,(int32_t)fv1.dir,this->fnBias + resIPDDMIRPGlue1Fn); - else if (fv0.dir < diagDir) // fv1.type set to line - swprintf(&buf[pos],L"%hi, %hi, %hi, %hi",(int32_t)fv0.dir,fv1.from,fv1.to,this->fnBias + resIPDDMIRPGlue2Fn); - else // both fv0.type and fv1.type set to line - swprintf(&buf[pos],L"%hi, %hi, %hi, %hi, %hi",fv0.from,fv0.to,fv1.from,fv1.to,this->fnBias + resIPDDMIRPGlue3Fn); - this->Emit(buf); - - this->ttv[fv] = fv1; - this->ttv[pv].dir = perpDiagDir; this->ttv[pv].from = parent0; this->ttv[pv].to = parent1; - this->usedpv = false; - this->lastChild = invalidKnotNum; - this->rp[0] = this->rp[1] = this->rp[2] = child1; // child1 MSIRP[m]ed last to re-center the median after asymmetric linking -} // TTSourceEngine::ResIPDDMIRP - -void TTSourceEngine::ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { - wchar_t buf[128]; - - swprintf(buf,L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi",grandParent0,grandParent1,parent0,parent1,child0,child1,cvt0,cvt1,this->fnBias + resIIPDMIRPFn); - this->Emit(buf); - - this->lastChild = invalidKnotNum; - - this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; // because don't know which branch taken - - this->round = rtg; - this->ttv[pv].dir = diagDir; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; // branch may or may not have been taken - this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->usedpv = false; -} // TTSourceEngine::ResIIPDMIRP - -void TTSourceEngine::ASM(bool inLine, wchar_t text[]) { - this->Emit(text); - if (inLine) this->InitTTEngineState(false); -} // TTSourceEngine::ASM - -void TTSourceEngine::INSTCTRL(short fromPpem, short toPpem) { - wchar_t code[512]; - - // GETINFO 6 below corresponding to ROTATEDINTERPRETERQUERY | STRETCHEDINTERPRETERQUERY, cf. Interp.c - // turn off instructions if outside ppem range or rasterizer version >= 1.8 and (rotated or stretched) - swprintf(code,L"#PUSHOFF" BRK - L"MPPEM[]" BRK - L"#PUSH, %hi" BRK - L"GT[]" BRK - L"MPPEM[]" BRK - L"#PUSH, %hi" BRK - L"LT[]" BRK - L"OR[]" BRK - L"#PUSH, 1" BRK - L"GETINFO[]" BRK - L"#PUSH, 37" BRK - L"GTEQ[]" BRK - L"#PUSH, 1" BRK - L"GETINFO[]" BRK - L"#PUSH, 64" BRK - L"LTEQ[]" BRK - L"AND[]" BRK - L"#PUSH, 6" BRK - L"GETINFO[]" BRK - L"#PUSH, 0" BRK - L"NEQ[]" BRK - L"AND[]" BRK - L"OR[]" BRK - L"IF[]" BRK - L" #PUSH, 1, 1" BRK - L" INSTCTRL[]" BRK - L"EIF[]" BRK - L"#PUSHON" BRK,toPpem,fromPpem); - - this->Emit(code); -} // TTSourceEngine::INSTCTRL - -void TTSourceEngine::SCANCTRL(short ctrl) { - wchar_t code[maxLineSize]; - - swprintf(code,L"SCANCTRL[], %hi" BRK,ctrl); - this->Emit(code); -} // TTSourceEngine::SCANCTRL - -void TTSourceEngine::SCANTYPE(short type) { - int32_t pos; - wchar_t code[maxLineSize]; - - pos = type <= 4 ? 0 : swprintf(code,L"SCANTYPE[], %hi" BRK,type-4); // Mac rasterizer doesn't handle types > 4 - swprintf(&code[pos],L"SCANTYPE[], %hi" BRK,type); - this->Emit(code); -} // TTSourceEngine::SCANTYPE - -void TTSourceEngine::SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]) { - short cvtCutIn; - wchar_t code[maxLineSize]; - - if(this->legacyCompile) - { - swprintf(code, L"WS[], 22, 1 /* s[22] = diagonal control: on (by default) */"); this->Emit(code); - } - - if (numCvtCutIns > 0) { - swprintf(code,L"SCVTCI[], %i",cvtCutInValue[0]); this->Emit(code); - if (!this->legacyCompile) - { - swprintf(code, L"WS[], %i, %i", cvtCutInStorage, cvtCutInValue[0]); this->Emit(code); - } - } - if (numCvtCutIns > 1) { - this->Emit(L"#PUSHOFF"); - cvtCutIn = 1; - while (cvtCutIn < numCvtCutIns) { - - if (!this->legacyCompile) - { - - swprintf(code, L"MPPEM[]" BRK - L"#PUSH, %hi" BRK - L"GTEQ[]" BRK - L"IF[]" BRK - L"#PUSH, %i, %i, %i" BRK - L"SCVTCI[]" BRK - L"WS[]", cvtCutInPpemSize[cvtCutIn], cvtCutInStorage, cvtCutInValue[cvtCutIn], cvtCutInValue[cvtCutIn]); - } - else - { - swprintf(code,L"MPPEM[]" BRK - L"#PUSH, %hi" BRK - L"GTEQ[]" BRK - L"IF[]" BRK - L"#PUSH, %i" BRK - L"SCVTCI[]",cvtCutInPpemSize[cvtCutIn],cvtCutInValue[cvtCutIn]); - } - - this->Emit(code); - cvtCutIn++; - if (this->legacyCompile && cvtCutIn == numCvtCutIns) { - swprintf(code,L"#PUSH, 22, 0" BRK - L"WS[] /* s[22] = diagonal control: off (by now) */"); this->Emit(code); - } - this->Emit( L"EIF[]"); - } - this->Emit(L"#PUSHON"); - this->Emit(L""); - } -} // TTSourceEngine::SCVTCI - -void TTSourceEngine::SetAspectRatioFlag(void) { - wchar_t code[maxLineSize]; - - this->Emit(L"/* Square aspect ratio? */"); - swprintf(code,L"CALL[], %hi",this->fnBias + aspectRatioFn); this->Emit(code); - this->Emit(L""); - this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv] = this->ttv[fv]; this->ttv[pv].dir = yRomanDir; - this->usedpv = false; -} // TTSourceEngine::SetAspectRatioFlag - -void TTSourceEngine::SetGreyScalingFlag(void) { - wchar_t code[maxLineSize]; - - this->Emit(L"/* Grey scaling? */"); - swprintf(code,L"CALL[], %hi",this->fnBias + greyScalingFn); this->Emit(code); - this->Emit(L""); -} // TTSourceEngine::SetGreyScalingFlag - -void TTSourceEngine::SetClearTypeCtrl(short ctrl) { - wchar_t code[maxLineSize]; - - if (ctrl > 0) { - swprintf(code,L"#PUSHOFF" BRK - L"#PUSH, 2, 2" BRK - L"RS[]" BRK - L"LTEQ[]" BRK - L"IF[]" BRK - L" #PUSH, %i, 3" BRK - L" INSTCTRL[]" BRK - L"EIF[]" BRK - L"#PUSHON" BRK,4); - } else { - swprintf(code,L"/* (font not tuned for ClearType) */" BRK); - } - this->Emit(code); -} // TTSourceEngine::SetClearTypeCtrl - -void TTSourceEngine::CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum) { - wchar_t code[maxLineSize]; - - if (relative) { - swprintf(code,L"CALL[], %hi, %hi, %hi, %hi",cvtNum,breakPpemSize,parentCvtNum,this->fnBias + doubleHeightFn); - this->sRound = false; this->round = rnone; // don't know which branch was taken... - } else { - swprintf(code,L"CALL[], %hi, %hi, %hi, %hi",cvtNum,parentCvtNum,breakPpemSize,this->fnBias + singleWeightFn); - } - this->Emit(code); -} // TTSourceEngine::CvtRegularization - -void TTSourceEngine::ResetPrepState(void) { - wchar_t code[16]; - - if (this->round != rtg) { - this->sRound = false; this->round = rtg; - this->Emit(L"RTG[]"); - } - if (this->deltaShift != defaultDeltaShift) { - this->deltaShift = defaultDeltaShift; - swprintf(code,L"SDS[], %hi",this->deltaShift); this->Emit(code); - } - if (this->deltaBase != defaultDeltaBase) { - this->deltaBase = defaultDeltaBase; - swprintf(code,L"SDB[], %hi",this->deltaBase); this->Emit(code); - } -} // TTSourceEngine::ResetPrepState - -void TTSourceEngine::SetFunctionNumberBias(short bias) { - this->fnBias = Max(0,bias); -} // TTSourceEngine::SetFunctionNumberBias - -short TTSourceEngine::GetFunctionNumberBias(void) { - return this->fnBias; -} // TTSourceEngine::GetFunctionNumberBias - -void TTSourceEngine::InitTTEngine(bool legacyCompile, bool *memError) { - this->error = false; - - this->InitTTEngineState(true); - this->fnBias = 0; - this->bufPos = 0; - this->bufLen = minBufLen; - this->buf = (wchar_t*)NewP(this->bufLen * sizeof(wchar_t)); - this->error = this->buf == NULL; - if (!this->error) this->buf[this->bufPos] = 0; // add trailing 0 assuming minBufLen > 0 - this->mov[false] = L'm'; this->min[false] = L'<'; this->rnd[false] = L'r'; - this->mov[true] = L'M'; this->min[true] = L'>'; this->rnd[true] = L'R'; - this->col[linkAnyColor][0] = L' '; this->col[linkGrey][0] = L'G'; this->col[linkBlack][0] = L'B'; this->col[linkWhite][0] = L'W'; - this->col[linkAnyColor][1] = L' '; this->col[linkGrey][1] = L'r'; this->col[linkBlack][1] = L'l'; this->col[linkWhite][1] = L'h'; - this->legacyCompile = legacyCompile; - *memError = this->error; -} // TTSourceEngine::InitTTEngine - -void TTSourceEngine::TermTTEngine(TextBuffer *glyfText, bool *memError) { - if (!this->error && glyfText) { - glyfText->Delete(0,glyfText->TheLength()); - glyfText->AppendRange(this->buf,0,this->bufPos); - } - if (this->buf != NULL) DisposeP((void**)&this->buf); - *memError = this->error; -} // TTSourceEngine::TermTTEngine - -TTSourceEngine::TTSourceEngine(void) { /* nix */ } /* TTSourceEngine::TTSourceEngine */ -TTSourceEngine::~TTSourceEngine(void) { /* nix */ } /* TTSourceEngine::~TTSourceEngine */ - -void TTSourceEngine::InitTTEngineState(bool forNewGlyph) { - if (forNewGlyph) { // at start of glyf program, assume TrueType engine's defaults - this->rp[0] = this->rp[1] = this->rp[2] = 0; - this->minDist = one6; - this->sRound = false; - this->round = rtg; - this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv] = this->ttv[fv]; - this->usedpv = false; - this->autoFlip = true; - this->deltaBase = defaultDeltaBase; this->deltaShift = defaultDeltaShift; - this->lastChild = invalidKnotNum; - } else { // after Call or Inline, assume any kind of side-effect - this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; - this->minDist = -1; - this->sRound = false; - this->round = rnone; - this->ttv[fv].dir = diagDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; - this->ttv[pv] = this->ttv[fv]; - this->usedpv = false; // ? - this->autoFlip = (bool)-1; - this->deltaBase = -48; this->deltaShift = -1; - this->lastChild = invalidKnotNum; - } -} // TTSourceEngine::InitTTEngineState - - -void GenGuardCond(TextBuffer *text, AltCodePath path) { - wchar_t codePath[32]; - - path = (AltCodePath)Min(Max(firstAltCodePath,path),lastAltCodePath); - - swprintf(codePath,L"#PUSH, %i, 2",path); text->AppendLine(codePath); - text->AppendLine(L"RS[]"); - swprintf(codePath,WIDE_STR_FORMAT L"EQ[]",path < altCodePathMonochromeOnly ? L"N" : L"LT"); text->AppendLine(codePath); -} // GenGuardCond - -void GenTalkIf(TextBuffer *talk, AltCodePath path, int32_t fpgmBias) { - wchar_t codePath[32]; - - talk->AppendLine(L"Inline(\x22"); - talk->AppendLine(L"#PUSHOFF"); - GenGuardCond(talk,path); - talk->AppendLine(L"IF[]"); - talk->AppendLine(L"#PUSHON"); - talk->AppendLine(L"#BEGIN"); - talk->AppendLine(L"\x22)"); - talk->AppendLine(L""); - swprintf(codePath,L"BeginCodePath(%i)",fpgmBias); talk->AppendLine(codePath); - talk->AppendLine(L""); -} // GenTalkIf - -void GenTalkElse(TextBuffer *talk, int32_t fpgmBias) { - wchar_t codePath[32]; - - talk->AppendLine(L""); - talk->AppendLine(L"EndCodePath()"); - talk->AppendLine(L""); - talk->AppendLine(L"Inline(\x22"); - talk->AppendLine(L"#END"); - talk->AppendLine(L"ELSE[]"); - talk->AppendLine(L"#BEGIN"); - talk->AppendLine(L"\x22)"); - talk->AppendLine(L""); - swprintf(codePath,L"BeginCodePath(%i)",fpgmBias); talk->AppendLine(codePath); - talk->AppendLine(L""); -} // GenTalkElse - -void GenTalkEndIf(TextBuffer *talk) { - talk->AppendLine(L""); - talk->AppendLine(L"EndCodePath()"); - talk->AppendLine(L""); - talk->AppendLine(L"Inline(\x22"); - talk->AppendLine(L"#END"); - talk->AppendLine(L"EIF[]"); - talk->AppendLine(L"\x22)"); -} // GenTalkEndIf - -void GenPrepIf(TextBuffer *prep, AltCodePath path) { - wchar_t codePath[32]; - - prep->AppendLine(L"#PUSHOFF"); - swprintf(codePath,L"#PUSH, %i",greyScalingFn); prep->AppendLine(codePath); - prep->AppendLine(L"CALL[]"); - GenGuardCond(prep,path); - prep->AppendLine(L"IF[]"); - prep->AppendLine(L"#PUSHON"); - prep->AppendLine(L"#BEGIN"); - prep->AppendLine(L""); -} // GenPrepIf - -void GenPrepElse(TextBuffer *prep) { - prep->AppendLine(L""); - prep->AppendLine(L"#END"); - prep->AppendLine(L"ELSE[]"); - prep->AppendLine(L"#BEGIN"); - prep->AppendLine(L""); -} // GenPrepElse - -void GenPrepEndIf(TextBuffer *prep) { - prep->AppendLine(L""); - prep->AppendLine(L"#END"); - prep->AppendLine(L"EIF[]"); -} // GenPrepEndIf - - -TTEngine *NewTTSourceEngine(void) { - return new TTSourceEngine; -} +/***** + + TTEngine.c - New TypeMan Talk Compiler - TrueType Engine + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +*****/ +#define _CRT_SECURE_NO_DEPRECATE + +#include // wcslen +#include // printf +#include "pch.h" +#include "CvtManager.h" // LinkColor +#include "TTEngine.h" + +#define MAX_HEIGHTS 37 + +#define minBufLen 1024L // should be > 0 and preferrably a power of 2 + +/***** THE FOLLOWING #defines SHOULD BE COORDINATED WITH AutomaticConstraintGenerator!!!!! *****/ + +#define cvtCutInStorage 3 + +#define setTTVtoXItalDirFn 9 +#define setTTVtoYItalDirFn 10 +#define setTTVtoXAdjItalDirFn 16 +#define setTTVtoYAdjItalDirFn 40 +#define oldDiagMirpFn 20 +#define deltaCvtPpemRangeFn 70 +#define deltaCvtSinglePpemFn 71 +#define deltaKnotPpemRangeFn 72 +#define deltaKnotSinglePpemFn 73 +#define colorDeltaCvtPpemRangeFn 74 +#define colorDeltaCvtSinglePpemFn 75 +#define colorDeltaKnotPpemRangeFn 76 +#define colorDeltaKnotSinglePpemFn 77 +#define offsetCvtFn (deltaCvtPpemRangeFn - deltaKnotPpemRangeFn) +#define threshold2UseDeltaFn 4 +#define defaultDeltaBase 9 // ppem +#define defaultDeltaShift 3 // 1/8 pixel +#define roundDownToGridBelowPpemFn 82 +#define aspectRatioFn 83 +#define greyScalingFn 84 +#define doubleHeightFn 85 +#define italicRiseRunFn 88 +#define singleWeightFn 31 + // 78 through 100 all used by AutomaticConstraintGenerator + // resActDistFn00 101 + // resActDistFn01 102 + // resActDistFn10 103 + // resActDistFn11 104 +#define resMIRPFn00 105 + // resMIRPFn01 106 + // resMIRPFn10 107 + // resMIRPFn11 108 + // symDistFn 109 + // ipMedianOffsetFn 110 + // ipMedianFn 111 + // resAdjStrokePhaseFn 112 + // deltaPhaseOptStrokePosFn 113 +#define resMIAPFn 114 +#define resIPMDAPFn 115 +#define resIPMIRPFn 116 +#define resIPDMIRPFn 117 + // reversePvFn 118 + // condAlignFn 119 +#define resDDMIRP0Fn 120 +#define resDDMIRP1Fn 121 +#define resDDMIRP2Fn 122 +#define resDDMIRP3Fn 123 + // whichSideOfEdgeFn 124 +#define resIPDDMIRPFn 125 +#define resIPDDMIRPGlue0Fn 126 +#define resIPDDMIRPGlue1Fn 127 +#define resIPDDMIRPGlue2Fn 128 +#define resIPDDMIRPGlue3Fn 129 + // epXtoBaseItalicFn 130 +#define resIIPDMIRPFn 131 + // regularizeLineFn 132 +#define iIUPLoopFn 133 +#define iIUPSimpleFn 134 +#define iIUPBoundaryFn 135 + + +DeltaColor DeltaColorOfByte(unsigned char byte) { + static DeltaColor deltaColorOfByte[0xE0] = { + /* 0 */ blackDelta, + /* 1 */ greyDelta, + /* 2 */ ctNatVerRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 6 */ ctComVerRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 10 */ ctNatHorRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 14 */ ctComHorRGBIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 18 */ ctNatVerBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 22 */ ctComVerBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 26 */ ctNatHorBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 30 */ ctComHorBGRIAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + /* 66 */ ctNatVerRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 70 */ ctComVerRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 74 */ ctNatHorRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 78 */ ctComHorRGBFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 82 */ ctNatVerBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 86 */ ctComVerBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 90 */ ctNatHorBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + /* 94 */ ctComHorBGRFAWBLYDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + /* 130 */ ctNatVerRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 134 */ ctComVerRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 138 */ ctNatHorRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 142 */ ctComHorRGBIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 146 */ ctNatVerBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 150 */ ctComVerBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 154 */ ctNatHorBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 158 */ ctComHorBGRIAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + invalidDelta, invalidDelta, invalidDelta, invalidDelta, + /* 194 */ ctNatVerRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 198 */ ctComVerRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 202 */ ctNatHorRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 206 */ ctComHorRGBFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 210 */ ctNatVerBGRFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 214 */ ctComVerBGRFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 218 */ ctNatHorBGRFAWYAADelta, invalidDelta, invalidDelta, invalidDelta, + /* 222 */ ctComHorBGRFAWYAADelta, invalidDelta + }; + + return 0 <= byte && byte < 0xE0 ? deltaColorOfByte[byte] : invalidDelta; +} // DeltaColorOfByte + +DeltaColor DeltaColorOfOptions(bool grayScale, bool clearType, bool clearTypeCompWidth, /* bool clearTypeVertRGB, */ bool clearTypeBGR, bool clearTypeFract, bool clearTypeYAA, bool clearTypeGray) { + unsigned char byte; + + // we'll assume that the combination of options is valid (the UI enforces this) + byte = 0; + if (grayScale) byte += 1; + if (clearType) byte += 2; + if (clearTypeCompWidth) byte += 4; +/* if (clearTypeVertRGB) byte += 8; */ + if (clearTypeBGR) byte += 0x10; + if (clearTypeFract) byte += 0x40; + if (clearTypeYAA) byte += 0x80; + return DeltaColorOfByte(byte); +} // DeltaColorOfOptions + +unsigned char ByteOfDeltaColor(DeltaColor color) { + static unsigned char byteOfDeltaColor[numDeltaColors] = { + 0x5f, 0, 1, + 2, 6, 10, 14, 18, 22, 26, 30, 66, 70, 74, 78, 82, 86, 90, 94, + 130, 134, 138, 142, 146, 150, 154, 158, 194, 198, 202, 206, 210, 214, 218, 222, + 0xdf }; + + return 0 <= color && color < numDeltaColors ? byteOfDeltaColor[color] : 0xdf /* ??? */; +} // ByteOfDeltaColor + +char *AllDeltaColorBytes(void) { + static char allBytes[0x100] = "0, 1, 2, 6, 10, 14, 18, 22, 26, 30, 66, 70, 74, 78, 82, 86, 90, 94, 130, 134, 138, 142, 146, 150, 154, 158, 194, 198, 202, 206, 210, 214, 218, or 222"; + + return &allBytes[0]; +} // AllDeltaColorBytes + +void TTEngine::Emit(const wchar_t text[]) { /* abstract */ } +void TTEngine::AssertFreeProjVector(TTVDirection dir) { /* abstract */ } +void TTEngine::AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot) { /* abstract */ } +void TTEngine::AssertFVonCA(bool y) { /* abstract */ } +void TTEngine::AssertPVonCA(bool y) { /* abstract */ } +void TTEngine::AssertFVonPV(void) { /* abstract */ } +TTVDirection TTEngine::FVDir(void) { /* abstract */ return xRomanDir; } +TTVDirection TTEngine::PVDir(void) { /* abstract */ return xRomanDir; } +void TTEngine::AssertRefPoint(short rp, short knot) { /* abstract */ } +void TTEngine::AssertRefPointPair(short rp0, short rp1, short knot0, short knot1) { /* abstract */ } +short TTEngine::AssertEitherRefPointOnKnot(short rp0, short rp1, short knot) { /* abstract */ return 0; } +short TTEngine::AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp) { /* abstract */ return 0; } +void TTEngine::AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]) { /* abstract */ } +void TTEngine::AssertAutoFlip(bool on) { /* abstract */ } +void TTEngine::AssertRounding(Rounding round) { /* abstract */ } +void TTEngine::AssertRoundingBelowPpem(Rounding round, short ppem) { /* abstract */ } +void TTEngine::AssertSuperRounding(short period, short phase, short thresHold) { /* abstract */ } +void TTEngine::RoundDownToGridBelowPpem(short ppem) { /* abstract */ } +void TTEngine::IfPpemBelow(short ppem) { /* abstract */ } +void TTEngine::Else(void) { /* abstract */ } +void TTEngine::End(bool invalidateRefPoints) { /* abstract */ } +void TTEngine::MDAP(bool round, short knot) { /* abstract */ } +void TTEngine::MIAP(bool round, short knot, short cvt) { /* abstract */ } +void TTEngine::MDRP(bool minDist, bool round, short color, short knot) { /* abstract */ } +void TTEngine::MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative) { /* abstract */ } +void TTEngine::DMIRP(short knot, short cvt, short pvFrom, short pvTo) { /* abstract */ } +void TTEngine::ALIGNRP(short knot) { /* abstract */ } +void TTEngine::IP(short knots, short knot[]) { /* abstract */ } +void TTEngine::SHP(short rp, short knots, short knot[]) { /* abstract */ } +void TTEngine::SHPIX(short knots, short knot[], F26Dot6 amount) { /* abstract */ } +void TTEngine::SLOOP(short count) { /* abstract */ } +void TTEngine::ISECT(short intersection, short line0start, short line0end, short line1start, short line1end) { /* abstract */ } +void TTEngine::IUP(bool y) { /* abstract */ } +void TTEngine::IPRange(bool y, short parent0, short parent1, short start, short end) { /* abstract */ } +void TTEngine::DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]) { /* abstract */ } +void TTEngine::CALL24(short leftCvt, short rightCvt) { /* abstract */ } +void TTEngine::CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1) { /* abstract */ } +void TTEngine::CALL64(short parent, short child, short cvt, bool half, bool flip) { /* abstract */ } +void TTEngine::CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip) { /* abstract */ } +void TTEngine::CALL678(bool back, short knot, short sameSide, short cvt, short storage) { /* abstract */ } +void TTEngine::CALL012345(short type, short knot0, short knot1, short knot2, short cvt) { /* abstract */ } +void TTEngine::CALL6(short knots, short knot[], short targetKnot) { /* abstract */ } +void TTEngine::CALL378(short type, short targetKnot) { /* abstract */ } +void TTEngine::CALL88(short riseCvt, short runCvt) { /* abstract */ } +void TTEngine::ResMIAP(short child, short cvt) { /* abstract */ } +void TTEngine::ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1) { /* abstract */ } +void TTEngine::ResMIRP(short parent, short child, short cvt, bool useMinDist) { /* abstract */ } +void TTEngine::ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1) { /* abstract */ } +void TTEngine::ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1) { /* abstract */ } +void TTEngine::ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { /* abstract */ } +void TTEngine::ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1) { /* abstract */ } +void TTEngine::ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { /* abstract */ } +void TTEngine::CALL(short actParams, short anyNum[], short functNum) { /* abstract */ } +void TTEngine::ASM(bool inLine, wchar_t text[]) { /* abstract */ } +void TTEngine::INSTCTRL(short fromPpem, short toPpem) { /* abstract */ } +void TTEngine::SCANCTRL(short ctrl) { /* abstract */ } +void TTEngine::SCANTYPE(short type) { /* abstract */ } +void TTEngine::SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]) { /* abstract */ } +void TTEngine::SetAspectRatioFlag(void) { /* abstract */ } +void TTEngine::SetGreyScalingFlag(void) { /* abstract */ } +void TTEngine::SetClearTypeCtrl(short ctrl) { /* abstract */ } +void TTEngine::CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum) { /* abstract */ } +void TTEngine::ResetPrepState(void) { /* abstract */ } +void TTEngine::SetFunctionNumberBias(short bias) { /* abstract */ } +short TTEngine::GetFunctionNumberBias(void) { return 0; /* abstract */ } +void TTEngine::InitTTEngine(bool legacyCompile, bool *memError) { /* abstract */ } +void TTEngine::TermTTEngine(TextBuffer *glyfText, bool *memError) { /* abstract */ } +TTEngine::TTEngine(void) { /* abstract */ } +TTEngine::~TTEngine(void) { /* abstract */ } + +class TTSourceEngine : public TTEngine { +public: + virtual void Emit(const wchar_t text[]); + virtual void AssertFreeProjVector(TTVDirection dir); + virtual void AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot); + virtual void AssertFVonCA(bool y); + virtual void AssertPVonCA(bool y); + virtual void AssertFVonPV(void); + virtual TTVDirection FVDir(void); + virtual TTVDirection PVDir(void); + virtual void AssertRefPoint(short rp, short knot); + virtual void AssertRefPointPair(short rp0, short rp1, short knot0, short knot1); // not necessarily in that order + virtual short AssertEitherRefPointOnKnot(short rp0, short rp1, short knot); // returns the ref point actually asserted + virtual short AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp); // returns the knot actually asserted + virtual void AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]); + virtual void AssertAutoFlip(bool on); + virtual void AssertRounding(Rounding round); + virtual void AssertRoundingBelowPpem(Rounding round, short ppem); + virtual void AssertSuperRounding(short period, short phase, short thresHold); + virtual void RoundDownToGridBelowPpem(short ppem); + virtual void IfPpemBelow(short ppem); + virtual void Else(void); + virtual void End(bool invalidateRefPoints); + virtual void MDAP(bool round, short knot); + virtual void MIAP(bool round, short knot, short cvt); + virtual void MDRP(bool minDist, bool round, short color, short knot); + virtual void MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative); + virtual void DMIRP(short knot, short cvt, short pvFrom, short pvTo); + virtual void ALIGNRP(short knot); + virtual void IP(short knots, short knot[]); + virtual void SHP(short rp, short knots, short knot[]); + virtual void SHPIX(short knots, short knot[], F26Dot6 amount); + virtual void SLOOP(short count); + virtual void ISECT(short intersection, short line0start, short line0end, short line1start, short line1end); + virtual void IUP(bool y); + virtual void IPRange(bool y, short parent0, short parent1, short start, short end); + virtual void DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]); + virtual void CALL24(short leftCvt, short rightCvt); + virtual void CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1); + virtual void CALL64(short parent, short child, short cvt, bool half, bool flip); // "special MIRP" for new italic strokes + virtual void CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip); + virtual void CALL678(bool back, short knot, short sameSide, short cvt, short storage); // new italic strokes: extrapolate knot to cvt height or back again + virtual void CALL012345(short type, short knot0, short knot1, short knot2, short cvt); + virtual void CALL6(short knots, short knot[], short targetKnot); + virtual void CALL378(short type, short targetKnot); + virtual void CALL(short actParams, short anyNum[], short functNum); + virtual void CALL88(short riseCvt, short runCvt); + virtual void ResMIAP(short child, short cvt); + virtual void ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1); + virtual void ResMIRP(short parent, short child, short cvt, bool useMinDist); + virtual void ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1); + virtual void ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1); + virtual void ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); + virtual void ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1); + virtual void ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); + virtual void ASM(bool inLine, wchar_t text[]); + virtual void INSTCTRL(short fromPpem, short toPpem); + virtual void SCANCTRL(short ctrl); + virtual void SCANTYPE(short type); + virtual void SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]); + virtual void SetAspectRatioFlag(void); + virtual void SetGreyScalingFlag(void); + virtual void SetClearTypeCtrl(short ctrl); + virtual void CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum); + virtual void ResetPrepState(void); + virtual void SetFunctionNumberBias(short bias); + virtual short GetFunctionNumberBias(void); + virtual void InitTTEngine(bool legacyCompile, bool *memError); + virtual void TermTTEngine(TextBuffer *glyfText, bool *memError); + TTSourceEngine(void); + virtual ~TTSourceEngine(void); +private: + void InitTTEngineState(bool forNewGlyph); // false => after Call or Inline + bool error; + short fnBias; + short rp[3]; // zp never changed + F26Dot6 minDist; + bool sRound; + Rounding round; + short period,phase,thresHold; + TTVectorDesc ttv[pv-fv+1],Ttv[pv-fv+1]; + bool usedpv,Usedpv; // I'm not sure whether this is always updated correctly upon reflecting all kinds of side-effects + bool autoFlip; + short deltaBase,deltaShift; + short lastChild; // to optimise the move flag in MDRP, MIRP + int32_t lastChildPos; // fixup position... + int32_t bufPos,bufLen; + wchar_t *buf; + wchar_t mov[2],min[2],rnd[2],col[numLinkColors][2]; // characters for encoding + bool legacyCompile; +}; + +void TTSourceEngine::Emit(const wchar_t text[]) { + int32_t len; + wchar_t *newBuf; + + if (this->error) return; // no further code generation possible... + len = (int32_t)STRLENW(text); + while (this->bufPos + len + 2 > this->bufLen) { // test for CR and trailing 0 + newBuf = (wchar_t*)NewP(2*this->bufLen * sizeof(wchar_t)); + this->error = newBuf == NULL; + if (this->error) return; // no further code generation possible... + memcpy(newBuf,this->buf,this->bufLen * sizeof(wchar_t)); + DisposeP((void**)&this->buf); + this->buf = newBuf; + this->bufLen = 2*this->bufLen; + } + memcpy(&this->buf[this->bufPos],text,len * sizeof(wchar_t)); + this->bufPos += len; + this->buf[this->bufPos++] = L'\x0D'; + this->buf[this->bufPos] = 0; // add trailing 0 +} // TTSourceEngine::Emit + +void TTSourceEngine::AssertFreeProjVector(TTVDirection dir) { + const TTVDirection fvDirMap[numTTVDirections] = {xRomanDir, yRomanDir, xRomanDir, yItalDir, xRomanDir, yAdjItalDir, diagDir, perpDiagDir}, + pvDirMap[numTTVDirections] = {xRomanDir, yRomanDir, xItalDir, yRomanDir, xAdjItalDir, yRomanDir, diagDir, perpDiagDir}; + wchar_t code[64]; + TTVDirection fvDir = fvDirMap[dir], + pvDir = pvDirMap[dir]; + + if (fvDir != this->ttv[fv].dir || pvDir != this->ttv[pv].dir) { + switch (dir) { + case xRomanDir: + swprintf(code,sizeof(code)/sizeof(wchar_t),L"SVTCA[X]"); + break; + case yRomanDir: + swprintf(code,sizeof(code) / sizeof(wchar_t), L"SVTCA[Y]"); + break; + case xItalDir: + swprintf(code,sizeof(code) / sizeof(wchar_t), L"CALL[], %i", this->fnBias + setTTVtoXItalDirFn); + break; + case yItalDir: + swprintf(code,sizeof(code) / sizeof(wchar_t), L"CALL[], %i", this->fnBias + setTTVtoYItalDirFn); + break; + case xAdjItalDir: + swprintf(code,sizeof(code) / sizeof(wchar_t), L"CALL[], %i", this->fnBias + setTTVtoXAdjItalDirFn); + break; + case yAdjItalDir: + swprintf(code,sizeof(code) / sizeof(wchar_t), L"CALL[], %i", this->fnBias + setTTVtoYAdjItalDirFn); + break; + default: + swprintf(code, sizeof(code) / sizeof(wchar_t), L"/* invalid TT vector direction */"); + break; + } + this->Emit(code); + this->ttv[fv].dir = fvDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv].dir = pvDir; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; + this->usedpv = false; + } +} // TTSourceEngine::AssertFreeProjVector + +void TTSourceEngine::AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot) { + TTVDirection dir; + wchar_t buf[32]; + TTVectorDesc *v = &this->ttv[ttv == fv ? fv : pv], *proj; + + if (P0.x == P1.x && P0.y != P1.y) dir = (TTVDirection)((short)yRomanDir - (short)rot); // incidently vertical + else if (P0.y == P1.y && P0.x != P1.x) dir = (TTVDirection)((short)xRomanDir + (short)rot); // ... horizontal + else dir = (TTVDirection)((short)diagDir + (short)rot); + if (dir < diagDir) { + if (dir != v->dir) { + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"S%cVTCA[%c]",ttv == fv ? L'F' : L'P',L'X' + (dir == yRomanDir)); this->Emit(buf); + v->dir = dir; v->from = v->to = invalidKnotNum; + if (ttv > fv) this->usedpv = ttv == dpv; + } + } else { + if (!(((v->from == parent0 && v->to == parent1) || (v->from == parent1 && v->to == parent0)) && dir == v->dir && (ttv != dpv || this->usedpv))) { + + proj = &this->ttv[pv]; + if (ttv == fv && ((proj->from == parent0 && proj->to == parent1) || (proj->from == parent1 && proj->to == parent0)) && proj->dir == dir) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SFVTPV[]"); + } else { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"S" WIDE_STR_FORMAT L"VTL[%c], %hi, %hi", ttv == fv ? L"F" : (ttv == pv ? L"P" : L"DP"), dir == perpDiagDir ? L'R' : L'r', parent0, parent1); + } + this->Emit(buf); + v->dir = dir; v->from = parent0; v->to = parent1; + if (ttv > fv) this->usedpv = ttv == dpv; + } + } +} // TTSourceEngine::AssertTTVonLine + +void TTSourceEngine::AssertFVonCA(bool y) { + Vector P0 = {0, 0}, P1 = {0, 0}; + + if (y) P1.y = 1; else P1.x = 1; + this->AssertTTVonLine(fv,invalidKnotNum,invalidKnotNum,P0,P1,false); +} // TTSourceEngine::AssertFVonCA + +void TTSourceEngine::AssertPVonCA(bool y) { + Vector P0 = {0, 0}, P1 = {0, 0}; + + if (y) P1.y = 1; else P1.x = 1; + this->AssertTTVonLine(pv,invalidKnotNum,invalidKnotNum,P0,P1,false); +} // TTSourceEngine::AssertPVonCA + +void TTSourceEngine::AssertFVonPV() { + if (this->ttv[fv].dir != this->ttv[pv].dir || this->ttv[fv].from != this->ttv[pv].from || this->ttv[fv].to != this->ttv[pv].to) { + this->Emit(L"SFVTPV[]"); + this->ttv[fv] = this->ttv[pv]; + } +} // TTSourceEngine::AssertFVonPV + +TTVDirection TTSourceEngine::FVDir(void) { + return this->ttv[fv].dir; +} // TTSourceEngine::FVDir + +TTVDirection TTSourceEngine::PVDir(void) { + return this->ttv[pv].dir; +} // TTSourceEngine::PVDir + +void TTSourceEngine::AssertRefPoint(short rp, short knot) { + wchar_t buf[32]; + + if (this->rp[rp] != knot) { + if (rp == 0 && this->lastChild == knot) { // set reference point by turning on the "move" flag + this->buf[this->lastChildPos] = this->mov[true]; // fixup + } else { // set reference point by the respective TT code... + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SRP%hi[], %hi", rp, knot); + this->Emit(buf); + } + this->rp[rp] = knot; + } + if (rp == 0) this->lastChild = invalidKnotNum; +} // TTSourceEngine::AssertRefPoint + +void TTSourceEngine::AssertRefPointPair(short rp0, short rp1, short knot0, short knot1) { + if (this->rp[rp0] == knot1 || this->rp[rp1] == knot0) { + this->AssertRefPoint(rp0,knot1); + this->AssertRefPoint(rp1,knot0); + } else { // and if we have to set the reference points, might as well assert sequentially + this->AssertRefPoint(rp0,knot0); + this->AssertRefPoint(rp1,knot1); + } +} // TTSourceEngine::AssertRefPointPair + +short TTSourceEngine::AssertEitherRefPointOnKnot(short rp0, short rp1, short knot) { + if (this->rp[rp1] == knot) return rp1; + else { this->AssertRefPoint(rp0,knot); return rp0; } +} // TTSourceEngine::AssertEitherRefPointOnKnot + +short TTSourceEngine::AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp) { + if (this->rp[rp] == knot1) return knot1; + else { this->AssertRefPoint(rp,knot0); return knot0; } +} // TTSourceEngine::AssertEitherKnotOnRefPoint + +void TTSourceEngine::AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]) { + wchar_t buf[32]; + + switch (minDists) { + case 1: + if (this->minDist != pixelSize[0]) { + swprintf(buf, sizeof(buf)/sizeof(wchar_t),L"SMD[], %i",pixelSize[0]); + this->Emit(buf); + this->minDist = pixelSize[0]; + } + break; + case 2: + this->Emit( L"MPPEM[]"); + if (this->minDist == pixelSize[1]) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"GT[], %hi, *", jumpPpemSize[1]); + this->Emit(buf); // [TOS](= jumpPpemSize[1]) > [TOS-1](= MPPEM[]) ? + this->Emit( L"IF[], *"); // MPPEM[] < jumpPpemSize[1] ? + this->Emit( L"#BEGIN"); // but current minDist is pixelSize[1] + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SMD[], %i", pixelSize[0]); + this->Emit(buf); // hence use pixelSize[0] instead + } else if (this->minDist == pixelSize[0]) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"LTEQ[], %hi, *", jumpPpemSize[1]); + this->Emit(buf); // [TOS](= jumpPpemSize[1]) ≤ [TOS-1](= MPPEM[]) ? + this->Emit( L"IF[], *"); // MPPEM[] ≥ jumpPpemSize[1] + this->Emit( L"#BEGIN"); // but current minDist is pixelSize[0] + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SMD[], %i", pixelSize[1]); + this->Emit(buf); // use pixelSize[1] instead + } else { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"GT[], %hi, *", jumpPpemSize[1]); + this->Emit(buf); + this->Emit( L"IF[], *"); + this->Emit( L"#BEGIN"); + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SMD[], %i", pixelSize[0]); + this->Emit(buf); + this->Emit( L"#END"); + this->Emit( L"ELSE[]"); + this->Emit( L"#BEGIN"); + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SMD[], %i", pixelSize[1]); + this->Emit(buf); + } + this->Emit( L"#END"); + this->Emit( L"EIF[]"); + this->minDist = -1; // undefined, because we don't know which branch was taken + break; + default: + break; + } +} // TTSourceEngine::AssertMinDist + +void TTSourceEngine::AssertAutoFlip(bool on) { + if (this->autoFlip != on) { + if (on) this->Emit(L"FLIPON[]"); else this->Emit(L"FLIPOFF[]"); + this->autoFlip = on; + } +} // TTSourceEngine::AssertAutoFlip + +void TTSourceEngine::AssertRounding(Rounding round) { + if (this->sRound || round != this->round) { + switch (round) { + case rthg: this->Emit(L"RTHG[]"); break; + case rtdg: this->Emit(L"RTDG[]"); break; + case rtg: this->Emit(L"RTG[]"); break; + case rdtg: this->Emit(L"RDTG[]"); break; + case rutg: this->Emit(L"RUTG[]"); break; + default: this->Emit(L"ROFF[]"); break; + } + this->sRound = false; this->round = round; + } +} // TTSourceEngine::AssertRounding + +void TTSourceEngine::AssertRoundingBelowPpem(Rounding round, short ppem) { + wchar_t buf[32]; + + if (round == roff || ppem < 0) + this->AssertRounding(round); + else { + this->Emit( L"MPPEM[]"); + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"GT[], *, %hi", ppem); + this->Emit(buf); + this->Emit( L"IF[], *"); + this->Emit( L"#BEGIN"); + switch (round) { + case rthg: this->Emit(L"RTHG[]"); break; + case rtdg: this->Emit(L"RTDG[]"); break; + case rtg: this->Emit(L"RTG[]"); break; + case rdtg: this->Emit(L"RDTG[]"); break; + case rutg: this->Emit(L"RUTG[]"); break; + default: this->Emit(L"ROFF[]"); break; + } + this->Emit( L"#END"); + this->Emit( L"ELSE[]"); + this->Emit( L"#BEGIN"); + this->Emit( L"ROFF[]"); + this->Emit( L"#END"); + this->Emit( L"EIF[]"); + this->sRound = false; this->round = rnone; + } +} // TTSourceEngine::AssertRoundingBelowPpem + +void TTSourceEngine::AssertSuperRounding(short period, short phase, short thresHold) { + wchar_t buf[32]; + + if (!this->sRound || period != this->period || phase != this->phase || thresHold != this->thresHold) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SROUND[], %hi", (period << 6) + (phase << 4) + (thresHold + 4)); + this->Emit(buf); // CEILING_ROUND never used, use threshold == -4 instead... + this->sRound = true; this->period = period; this->phase = phase; this->thresHold = thresHold; + } +} // TTSourceEngine::AssertSuperRounding + +void TTSourceEngine::RoundDownToGridBelowPpem(short ppem) { + wchar_t buf[32]; + + if (ppem < 0) + this->AssertRounding(rdtg); + else { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi", ppem, this->fnBias + roundDownToGridBelowPpemFn); + this->Emit(buf); + this->sRound = false; this->round = rnone; + } +} // TTSourceEngine::RoundDownToGridBelowPpem + +void TTSourceEngine::IfPpemBelow(short ppem) { // this is not nestable, nor general enough to deal with the entire graphics state, but can grow later, as needed + wchar_t buf[32]; + + this->Emit( L"MPPEM[]"); + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"GT[], *, %hi", ppem); + this->Emit(buf); + this->Emit( L"IF[], *"); + this->Emit( L"#BEGIN"); + this->Ttv[fv] = this->ttv[fv]; + this->Ttv[pv] = this->ttv[pv]; + this->Usedpv = this->usedpv; +} // TTSourceEngine::IfPpemBelow + +void TTSourceEngine::Else(void) { + this->Emit( L"#END"); + this->Emit( L"ELSE[]"); + this->Emit( L"#BEGIN"); + this->ttv[fv] = this->Ttv[fv]; + this->ttv[pv] = this->Ttv[pv]; + this->usedpv = this->Usedpv; +} // TTSourceEngine::Else + +void TTSourceEngine::End(bool invalidateRefPoints) { + this->Emit( L"#END"); + this->Emit( L"EIF[]"); + this->ttv[fv].dir = diagDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; // assume any kind of side-effect, cf. InitTTEngine(true) + this->ttv[pv] = this->ttv[fv]; + this->usedpv = false; // ? + if (invalidateRefPoints) this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; +} // TTSourceEngine::End + +void TTSourceEngine::MDAP(bool round, short knot) { + wchar_t buf[32]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"MDAP[%c], %hi", this->rnd[round], knot); + this->Emit(buf); + this->rp[0] = this->rp[1] = knot; this->lastChild = invalidKnotNum; +} // TTSourceEngine::MDAP + +void TTSourceEngine::MIAP(bool round, short knot, short cvt) { + wchar_t buf[32]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"MIAP[%c], %hi, %hi", this->rnd[round], knot, cvt); + this->Emit(buf); + this->rp[0] = this->rp[1] = knot; this->lastChild = invalidKnotNum; +} // TTSourceEngine::MIAP + +void TTSourceEngine::MDRP(bool minDist, bool round, short color, short knot) { + wchar_t buf[32]; + + this->lastChild = knot; + this->lastChildPos = this->bufPos + 5; // a bit of a hack, but just a small one... + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"MDRP[%c%c%c%c%c], %hi", this->mov[false], this->min[minDist], this->rnd[round], this->col[color][0], this->col[color][1], knot); + this->Emit(buf); + this->rp[1] = this->rp[0]; this->rp[2] = knot; +} // TTSourceEngine::MDRP + +void TTSourceEngine::MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative) { + wchar_t buf[32]; + + if (negative) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"RCVT[], %hi", cvt); + this->Emit(buf); + this->Emit( L"NEG[], *"); + cvt = SECONDTMPCVT; + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"WCVTP[], %hi, *", cvt); + this->Emit(buf); + } + this->lastChild = knot; + this->lastChildPos = this->bufPos + 5; // a bit of a hack, but just a small one... + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"MIRP[%c%c%c%c%c], %hi, %hi", this->mov[false], this->min[minDist], this->rnd[round], this->col[color][0], this->col[color][1], knot, cvt); + this->Emit(buf); + this->rp[1] = this->rp[0]; this->rp[2] = knot; +} // TTSourceEngine::MIRP + +void TTSourceEngine::DMIRP(short knot, short cvt, short pvFrom, short pvTo) { + wchar_t buf[64]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hd, %hd, %hd, %hd, %hi", knot, cvt, pvFrom, pvTo, this->fnBias + oldDiagMirpFn); + this->Emit(buf); + this->rp[1] = this->rp[0]; this->rp[2] = knot; this->lastChild = invalidKnotNum; + this->round = rtg; + this->ttv[pv].dir = perpDiagDir; this->ttv[pv].from = pvFrom; this->ttv[pv].to = pvTo; + this->usedpv = true; +} // TTSourceEngine::DMIRP + +void TTSourceEngine::ALIGNRP(short knot) { + wchar_t buf[32]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"ALIGNRP[], %hi", knot); + this->Emit(buf); +} // TTSourceEngine::ALIGNRP + +void TTSourceEngine::IP(short knots, short knot[]) { + wchar_t buf[8*maxParams]; + short i; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"IP[]"); + for (i = 0; i < knots; i++) + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L", %hi", knot[i]); + this->Emit(buf); +} // TTSourceEngine::IP + +void TTSourceEngine::SHP(short rp, short knots, short knot[]) { + wchar_t buf[8*maxParams]; + short i; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SHP[%hi]", rp); + for (i = 0; i < knots; i++) + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L", %hi", knot[i]); + this->Emit(buf); +} // TTSourceEngine::SHP + +void TTSourceEngine::SHPIX(short knots, short knot[], F26Dot6 amount) { + wchar_t buf[8*maxParams]; + short i; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SHPIX[]"); + for (i = 0; i < knots; i++) + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L", %hi", knot[i]); + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L", %i", amount); + this->Emit(buf); +} // TTSourceEngine::SHPIX + +void TTSourceEngine::SLOOP(short count) { + wchar_t buf[32]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SLOOP[], %hi", count); + this->Emit(buf); +} // TTSourceEngine::SLOOP + +void TTSourceEngine::ISECT(short intersection, short line0start, short line0end, short line1start, short line1end) { + wchar_t buf[48]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"ISECT[], %hi, %hi, %hi, %hi, %hi", intersection, line0start, line0end, line1start, line1end); + this->Emit(buf); +} // TTSourceEngine::ISECT + +void TTSourceEngine::IUP(bool y) { + wchar_t buf[8]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"IUP[%c]", L'X' + (y & 1)); + this->Emit(buf); +} // TTSourceEngine::IUP + +void TTSourceEngine::IPRange(bool y, short parent0, short parent1, short start, short end) { + wchar_t buf[64]; + + if (parent0 <= parent1) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi", parent0, parent1, this->fnBias + iIUPSimpleFn); + this->Emit(buf); + } else { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi", parent0, end, start, parent1, this->fnBias + iIUPBoundaryFn); + this->Emit(buf); + } +} // TTSourceEngine::IPRange + +/***** +void FifthLoop(short *ppem, bool ppemSize[]); +void FifthLoop(short *ppem, bool ppemSize[]) { + do (*ppem)++; while (*ppem < maxPpemSize && !ppemSize[*ppem]); // next ppem to do +} + +void TTSourceEngine::DLT(short knot, F26Dot6 amount, bool ppemSize[]) { + short amnt,sign,steps,maxShift,magnitude[3],shift[3],ppem,deltaSubBase,deltas; + char buf[128]; + +// with a maximum esolution of 1 and a minimum resolution of 1/64, we can delta in range -8..+8 in steps of 1/64. +// this means that upto 3 steps have to be performed: doing the entire pixels, doing the 1/8 pixels, and finally +// doing the 1/64. If we delta by, say, 33/32, this can be done in two steps, e.g. 4/4 and 1/32. Likewise, 65/64 +// can be done as 1/1 and 1/64 + + amnt = (short)Abs(amount); sign = Sgn(amount); steps = 0; maxShift = 6; +// old version +// while (amnt != 0) { +// while (maxShift > 0 && (amnt & 1) == 0) { amnt >>= 1; maxShift--; } +// if (amnt > 8) { +// magnitude[steps] = sign*(amnt & 7); amnt = amnt >> 3; +// } else { // allow 8/1 (or e.g. 8/8 in a stream of other deltas with delta shift = 3) +// magnitude[steps] = sign*amnt; amnt = 0; +// } +// shift[steps] = maxShift; maxShift = Max(0,maxShift-3); +// steps++; +// } +// end of old version + if ((amnt & (short)0xffff << 6-this->deltaShift) == amnt && amnt >> 6-this->deltaShift <= 8) { // amount is "addressable" by current delta shift + magnitude[steps] = sign*(amnt >> 6-this->deltaShift); + shift[steps] = this->deltaShift; + steps++; + } else if ((amnt & (short)0xffff << stdDeltaShift) == amnt && amnt >> stdDeltaShift <= 8) { // amount is "addressable" by standard delta shift + magnitude[steps] = sign*(amnt >> stdDeltaShift); + shift[steps] = stdDeltaShift; + steps++; + } else { + while (amnt > 0) { + while (amnt > 8 && (amnt & 1) == 0) { amnt >>= 1; maxShift--; } + if (amnt > 8) { + magnitude[steps] = sign*(amnt & 7); amnt = amnt >> 3; + } else { // allow 8/1 (or e.g. 8/8 in a stream of other deltas with delta shift = 3) + magnitude[steps] = sign*amnt; amnt = 0; + } + shift[steps] = maxShift; maxShift = Max(0,maxShift-3); + steps++; + } + } + while (steps > 0) { // while there are steps to do + steps--; + if (this->deltaShift != shift[steps]) { // resolution is not the right one for this step + this->deltaShift = shift[steps]; + swprintf(buf,L"SDS[], %hi",this->deltaShift); this->Emit(buf); + } + ppem = 0; + while (ppem < maxPpemSize && !ppemSize[ppem]) ppem++; + while (ppem < maxPpemSize) { // while there are ppems to do + if (ppem < this->deltaBase || this->deltaBase + 47 < ppem) { // ppem is outside range "addressable" by DLTP1, DLTP2, or DLTP3 + this->deltaBase = ppem; + swprintf(buf,L"SDB[], %hi",this->deltaBase); this->Emit(buf); + } + deltaSubBase = this->deltaBase; + while (deltaSubBase + 15 < ppem) deltaSubBase += 16; + while (ppem < maxPpemSize && deltaSubBase <= this->deltaBase + 47) { // while there are ppems to do and they are "addressable" by DLTP1, DLTP2, or DLTP3 + if (ppem <= deltaSubBase + 15) { // if there are ppems to do which are "addressable" by "current" DLTPi + swprintf(buf,L"DLTP%hi[",((deltaSubBase-this->deltaBase) >> 4) + 1); deltas = 0; + do { + if (deltas == 4) { this->Emit(buf); swprintf(buf,L" "); deltas = 0; } + swprintf(&buf[STRLENW(buf)],L"(%hi @%hi %hi)",knot,ppem-deltaSubBase,magnitude[steps]); deltas++; + + FifthLoop(&ppem,ppemSize); // Think C compiler 7.0 doesn't like a 5th loop with code optimizations turned on, 7.0.3 does, though... + + // do ppem++; while (ppem < maxPpemSize && !ppemSize[ppem]); // next ppem to do + + } while (ppem <= deltaSubBase + 15); // while there are ppems to do which are "addressable" by "current" DLTPi + swprintf(&buf[STRLENW(buf)],L"]"); + this->Emit(buf); + } + deltaSubBase += 16; + } + } + } +} // TTSourceEngine::DLT +*****/ + +void SplitPpemSize(short threshold, bool ppemSize[], short *singleSizes, short singleSize[], short *ranges, short rangeLow[], short rangeHigh[]); +void SplitPpemSize(short threshold, bool ppemSize[], short *singleSizes, short singleSize[], short *ranges, short rangeLow[], short rangeHigh[]) { + short ppem,low; + + *singleSizes = *ranges = ppem = 0; + while (ppem < maxPpemSize && !ppemSize[ppem]) ppem++; + while (ppem < maxPpemSize) { + low = ppem; + while (ppem < maxPpemSize && ppemSize[ppem]) ppem++; + if (ppem - low >= threshold) { + rangeLow[*ranges] = low; + rangeHigh[(*ranges)++] = ppem-1; + } else { + while (low < ppem) + singleSize[(*singleSizes)++] = low++; + } + while (ppem < maxPpemSize && !ppemSize[ppem]) ppem++; + } +} // SplitPpemSize + +void TTSourceEngine::DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]) { + short amnt,sign,magnitude,shift,size,deltaSubBase,deltas; + bool singleStep; + wchar_t charFn,buf[128]; + short singlePpemSizes,singlePpemSize[maxPpemSize],ppemRanges,ppemRangeLow[maxPpemSize],ppemRangeHigh[maxPpemSize],offsFn; + +// with a maximum resolution of 1 and a minimum resolution of 1/64, we can delta in range -8..+8 in steps of 1/64. +// this means that upto 3 steps have to be performed: doing the entire pixels, doing the 1/8 pixels, and finally +// doing the 1/64. If we delta by, say, 33/32, this can be done in two steps, e.g. 4/4 and 1/32. Likewise, 65/64 +// can be done as 1/1 and 1/64 + + amnt = (short)Abs(amount); sign = Sgn(amount); + if (this->deltaShift >= 0 && (amnt & (short)0xffff << (6-this->deltaShift)) == amnt && amnt >> (6-this->deltaShift) <= 8) { // amount is "addressable" by current delta shift + magnitude = sign*(amnt >> (6-this->deltaShift)); + shift = this->deltaShift; + singleStep = true; + } else if (this->deltaShift >= 0 && (amnt & (short)0xffff << stdDeltaShift) == amnt && amnt >> stdDeltaShift <= 8) { // amount is "addressable" by standard delta shift + magnitude = sign*(amnt >> stdDeltaShift); + shift = stdDeltaShift; + singleStep = true; + } else { // may need a new delta shift, if we get away with a single delta at all + shift = 6; + while (amnt > 8 && (amnt & 1) == 0) { amnt >>= 1; shift--; } + magnitude = sign*amnt; + singleStep = amnt <= 8; + } + +// if it takes 2 steps (or more) to do a delta, we need two SDS[] and two DLTPi using 12 bytes (or three of each using 18 bytes), +// as opposed to a function call using 6 to 9 bytes only (6 for positive amount, 9 for negative due to sign extension and prepush + SplitPpemSize(singleStep && color == alwaysDelta ? threshold2UseDeltaFn : 1,ppemSize,&singlePpemSizes,singlePpemSize,&ppemRanges,ppemRangeLow,ppemRangeHigh); + + if (ppemRanges > 0) { + offsFn = cvt ? offsetCvtFn : 0; + for (size = 0; size < ppemRanges; size++) { + if (ppemRangeLow[size] == ppemRangeHigh[size]) + if (color == alwaysDelta) + if (cvt) + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"CALL[], %i, %hi, %hi, %hi",amount,knot,ppemRangeLow[size],this->fnBias + deltaCvtSinglePpemFn); + else + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %i, %hi, %hi", knot, amount, ppemRangeLow[size], this->fnBias + deltaKnotSinglePpemFn); + else + if (cvt) + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %i, %hi, %hi, %hi, %hi", amount, knot, ppemRangeLow[size], ByteOfDeltaColor(color), this->fnBias + colorDeltaCvtSinglePpemFn); + else + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %i, %hi, %hi, %hi", knot, amount, ppemRangeLow[size], ByteOfDeltaColor(color), this->fnBias + colorDeltaKnotSinglePpemFn); + else + if (color == alwaysDelta) + if (cvt) + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %i, %hi, %hi, %hi, %hi", amount, knot, ppemRangeLow[size], ppemRangeHigh[size], this->fnBias + deltaCvtPpemRangeFn); + else + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %i, %hi, %hi, %hi", knot, amount, ppemRangeLow[size], ppemRangeHigh[size], this->fnBias + deltaKnotPpemRangeFn); + else + if (cvt) + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %i, %hi, %hi, %hi, %hi, %hi", amount, knot, ppemRangeLow[size], ppemRangeHigh[size], ByteOfDeltaColor(color), this->fnBias + colorDeltaCvtPpemRangeFn); + else + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %i, %hi, %hi, %hi, %hi", knot, amount, ppemRangeLow[size], ppemRangeHigh[size], ByteOfDeltaColor(color), this->fnBias + colorDeltaKnotPpemRangeFn); + this->Emit(buf); + } + } + if (singlePpemSizes > 0) { + charFn = cvt ? L'C' : L'P'; + if (this->deltaShift != shift) { // resolution is not the right one for this step + this->deltaShift = shift; + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SDS[], %hi", this->deltaShift); + this->Emit(buf); + } + size = 0; + while (size < singlePpemSizes) { // while there are ppems to do + if (singlePpemSize[size] < this->deltaBase || this->deltaBase + 47 < singlePpemSize[size]) { // ppem is outside range "addressable" by DLTP1, DLTP2, or DLTP3 + this->deltaBase = singlePpemSize[size]; + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"SDB[], %hi", this->deltaBase); + this->Emit(buf); + } + deltaSubBase = this->deltaBase; + while (size < singlePpemSizes && deltaSubBase <= this->deltaBase + 47) { // while there are ppems to do and they are "addressable" by DLTP1, DLTP2, or DLTP3 + if (size < singlePpemSizes && singlePpemSize[size] <= deltaSubBase + 15) { // if there are ppems to do which are "addressable" by "current" DLTPi + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"DLT%c%hi[",charFn,((deltaSubBase-this->deltaBase) >> 4) + 1); deltas = 0; + do { + if (deltas == 4) + { + this->Emit(buf); + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L" "); + deltas = 0; + } + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L"(%hi @%hi %hi)", knot, singlePpemSize[size] - deltaSubBase, magnitude); + deltas++; + size++; + } while (size < singlePpemSizes && singlePpemSize[size] <= deltaSubBase + 15); // while there are ppems to do which are "addressable" by "current" DLTPi + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L"]"); + this->Emit(buf); + } + deltaSubBase += 16; + } + } + } +} // TTSourceEngine::DLT + +void TTSourceEngine::CALL24(short leftCvt, short rightCvt) { + wchar_t buf[32]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi", leftCvt, rightCvt, this->fnBias + 24); + this->Emit(buf); +} // TTSourceEngine::CALL24 + +void TTSourceEngine::CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1) { + wchar_t buf[64]; + + switch (type) { + case 34: + case 35: + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi", knot3, cvt3, cvt2, knot1, cvt1, this->fnBias + type); + break; + case 36: + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi", knot3, cvt3, knot2, cvt2, knot1, cvt1, this->fnBias + type); + break; + } + this->Emit(buf); + this->ttv[fv].dir = yRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv] = this->ttv[fv]; + this->rp[0] = knot1; this->lastChild = invalidKnotNum; + this->rp[1] = knot1; + this->rp[2] = knot3; +} // TTSourceEngine::CALL3456 + +void TTSourceEngine::CALL64(short parent, short child, short cvt, bool half, bool flip) { + wchar_t buf[64]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi", parent, child, cvt, half, flip, this->fnBias + 64); + this->Emit(buf); + this->rp[0] = this->rp[1] = parent; this->lastChild = invalidKnotNum; + this->rp[2] = child; +} // TTSourceEngine::CALL64 + +void TTSourceEngine::CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip) { + wchar_t buf[64]; + TTVectorDesc *v; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi", knot0, knot1, knot2, knot3, cvt, storage, (short)xLinks, (short)flip, this->fnBias + 65 + (short)crissCrossLinks); + this->Emit(buf); + this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; this->lastChild = invalidKnotNum; + v = &this->ttv[pv]; v->dir = diagDir; v->from = v->to = invalidKnotNum; // assume any kind of side-effect, cf. InitTTEngine(true) + this->ttv[fv] = this->ttv[pv]; +} // TTSourceEngine::CALL656 + +void TTSourceEngine::CALL678(bool back, short knot, short sameSide, short cvt, short storage) { + wchar_t buf[64]; + TTVectorDesc *v; + + if (back) + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi", knot, sameSide, storage, this->fnBias + 68); + else + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi", knot, sameSide, cvt, storage, this->fnBias + 67); + this->Emit(buf); + v = &this->ttv[pv]; v->dir = diagDir; v->from = v->to = invalidKnotNum; // assume any kind of side-effect, cf. InitTTEngine(true) + this->ttv[fv] = this->ttv[pv]; + this->ttv[pv].dir = yRomanDir; // no reference points changed +} // TTSourceEngine::CALL678 + +void TTSourceEngine::CALL012345(short type, short knot0, short knot1, short knot2, short cvt) { + wchar_t buf[64]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi", knot0, knot1, knot2, cvt, this->fnBias + type); + this->Emit(buf); + // no side-effects +} // TTSourceEngine::CALL012345 + +void TTSourceEngine::CALL6(short knots, short knot[], short targetKnot) { + short i; + wchar_t buf[256]; + + if (knots > 1) { + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"LOOPCALL[]"); + for (i = 0; i < knots; i++) swprintf(&buf[STRLENW(buf)],sizeof(buf)/sizeof(wchar_t),L", %hi, %hi",knot[i],targetKnot); + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L", %hi, %hi", knots, this->fnBias + 6); + } else if (knots == 1) + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi", knot[0], targetKnot, this->fnBias + 6); + if (knots > 0) { + this->rp[0] = targetKnot; this->lastChild = invalidKnotNum; + this->ttv[fv].dir = yRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv] = this->ttv[fv]; + this->Emit(buf); + } +} // TTSourceEngine::CALL6 + +void TTSourceEngine::CALL378(short type, short targetKnot) { + wchar_t buf[32]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi", targetKnot, this->fnBias + type); + this->Emit(buf); + this->rp[0] = type == 37 ? targetKnot + 1 : targetKnot - 1; this->lastChild = invalidKnotNum; + this->ttv[fv].dir = yRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv] = this->ttv[fv]; +} // TTSourceEngine::CALL378 + +void TTSourceEngine::CALL(short actParams, short anyNum[], short functNum) { + wchar_t buf[8*maxParams]; + short i; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[]"); + for (i = 0; i < actParams; i++) + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L", %hi", anyNum[i]); + swprintf(&buf[STRLENW(buf)], sizeof(buf) / sizeof(wchar_t), L", %hi", functNum); // here functNum is passed explicitly as a parameter, hence we expect caller to correctly bias function number + this->Emit(buf); + this->InitTTEngineState(false); +} // TTSourceEngine::CALL + +void TTSourceEngine::CALL88(short riseCvt, short runCvt) { + wchar_t buf[32]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi", riseCvt, runCvt, this->fnBias + italicRiseRunFn); + this->Emit(buf); + this->rp[0] = this->rp[1] = invalidKnotNum; this->lastChild = invalidKnotNum; + this->sRound = false; this->round = rtg; + this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv] = this->ttv[fv]; +} // TTSourceEngine::CALL88 + +void TTSourceEngine::ResMIAP(short child, short cvt) { + wchar_t buf[64]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi", child, cvt, this->fnBias + resMIAPFn); + this->Emit(buf); + + this->lastChild = invalidKnotNum; // MSIRP in fpgm, cannot patch code in fpgm... + this->rp[0] = this->rp[1] = this->rp[2] = child; // MSIRP[M] used in fpgm +} // TTSourceEngine::ResMIAP + +void TTSourceEngine::ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1) { + wchar_t buf[64]; + short dirFlag; + + dirFlag = ((short)postRoundFlag)*8 + (short)pvP; + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi", dirFlag, parent0, child, parent1, this->fnBias + resIPMDAPFn); + this->Emit(buf); + + // SO FAR, WILL NEED TO MAKE MORE GENERAL TO ACCOMODATE fvP and fvC + if (postRoundFlag) { // only allowed on ResXIPAnchor + this->ttv[fv].dir = xRomanDir; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; + this->ttv[fv] = this->ttv[pv]; + this->rp[1] = child; + } else { + this->ttv[pv].dir = pvP; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; + this->ttv[fv] = this->ttv[pv]; + this->rp[1] = parent0; + } + this->usedpv = false; + this->lastChild = invalidKnotNum; + this->rp[0] = this->rp[2] = child; + // parent1; +} // TTSourceEngine::ResIPMDAP + +void TTSourceEngine::ResMIRP(short parent, short child, short cvt, bool useMinDist) { + bool useCvt; + int32_t pos; + wchar_t buf[64]; + + useCvt = cvt != invalidCvtNum; + pos = swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi", parent, child); + if (useCvt) + pos += swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L", %hi", cvt); + pos += swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L", %hi", this->fnBias + resMIRPFn00 + 2 * (int32_t)useCvt + (int32_t)useMinDist); + + this->Emit(buf); + + this->lastChild = invalidKnotNum; // MSIRP in fpgm, cannot patch code in fpgm... + this->rp[1] = parent; + this->rp[0] = this->rp[2] = child; // MSIRP[M] used in fpgm +} // TTSourceEngine::ResMIRP + +void TTSourceEngine::ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1) { + wchar_t buf[64]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi", pvGP, grandParent0, parent, child, cvt, grandParent1, strokeOptimizationFlag, this->fnBias + resIPMIRPFn); + this->Emit(buf); + + this->ttv[pv].dir = (TTVDirection)((int32_t)pvGP % 2); this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; + this->ttv[fv] = this->ttv[pv]; // SO FAR, WILL NEED TO MAKE MORE GENERAL TO ACCOMODATE fvP and fvC + this->usedpv = false; + this->lastChild = invalidKnotNum; + this->rp[0] = this->rp[1] = this->rp[2] = parent; // parent SRP0[]ed and MSIRP[m]ed last in emulate interpolation of median +} // TTSourceEngine::ResIPMIRP + +void TTSourceEngine::ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1) { + wchar_t buf[128]; + int32_t pos; + + // CAUTION: this scheme doesn't support setting the fv PERPENDICULAR to a line (so far it doesn't need to) + pos = swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, ", parent0, child0, parent1, child1, cvt0, cvt1); + if (fv0.dir <= yRomanDir && fv1.dir <= yRomanDir) // simple case + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi", (short)fv0.dir, (short)fv1.dir, this->fnBias + resDDMIRP0Fn); + else if (fv0.dir <= yRomanDir) // fv1.type set to line + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi, %hi", (short)fv0.dir, fv1.from, fv1.to, this->fnBias + resDDMIRP1Fn); + else if (fv1.dir <= yRomanDir) // fv0.type set to line + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi, %hi", fv0.from, fv0.to, (short)fv1.dir, this->fnBias + resDDMIRP2Fn); + else // both fv0.type and fv1.type set to line + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi, %hi, %hi", fv0.from, fv0.to, fv1.from, fv1.to, this->fnBias + resDDMIRP3Fn); + this->Emit(buf); + + this->lastChild = invalidKnotNum; + this->rp[0] = parent0; // this->rp[1] = this->rp[2] = invalidKnotNum; unchanged + this->ttv[fv] = fv1; + this->ttv[pv].dir = perpDiagDir; this->ttv[pv].from = parent0; this->ttv[pv].to = child1; + this->usedpv = false; +} // TTSourceEngine::ResDDMIRP + +void TTSourceEngine::ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { + wchar_t buf[128]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi", pvGP, grandParent0, parent0, child0, cvt0, parent1, child1, cvt1, grandParent1, this->fnBias + resIPDMIRPFn); + this->Emit(buf); + + this->ttv[pv].dir = (TTVDirection)((int32_t)pvGP % 2); this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; + this->ttv[fv] = this->ttv[pv]; // so far; may have to make more general to accomodate generalized freedom vectors used by the last knot moved, or have fn reset state to pvGP + this->usedpv = false; + this->lastChild = invalidKnotNum; + this->rp[0] = this->rp[1] = this->rp[2] = parent1; // parent1 SRP0[]ed and MSIRP[m]ed last in symDistFn +} // TTSourceEngine::ResIPDMIRP + +void TTSourceEngine::ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1) { + wchar_t buf[128]; + int32_t pos; + + // CAUTION: this scheme doesn't support setting the fv PERPENDICULAR to a line (so far it doesn't need to) + pos = swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, ", pvGP, grandParent0, parent0, child0, cvt0, parent1, child1, cvt1, grandParent1); + if (fv0.dir < diagDir && fv1.dir < diagDir) // simple case + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi", (int32_t)fv0.dir, (int32_t)fv1.dir, this->fnBias + resIPDDMIRPGlue0Fn); + else if (fv1.dir < diagDir) // fv0.type set to line + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi, %hi", fv0.from, fv0.to, (int32_t)fv1.dir, this->fnBias + resIPDDMIRPGlue1Fn); + else if (fv0.dir < diagDir) // fv1.type set to line + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi, %hi", (int32_t)fv0.dir, fv1.from, fv1.to, this->fnBias + resIPDDMIRPGlue2Fn); + else // both fv0.type and fv1.type set to line + swprintf(&buf[pos], sizeof(buf) / sizeof(wchar_t), L"%hi, %hi, %hi, %hi, %hi", fv0.from, fv0.to, fv1.from, fv1.to, this->fnBias + resIPDDMIRPGlue3Fn); + this->Emit(buf); + + this->ttv[fv] = fv1; + this->ttv[pv].dir = perpDiagDir; this->ttv[pv].from = parent0; this->ttv[pv].to = parent1; + this->usedpv = false; + this->lastChild = invalidKnotNum; + this->rp[0] = this->rp[1] = this->rp[2] = child1; // child1 MSIRP[m]ed last to re-center the median after asymmetric linking +} // TTSourceEngine::ResIPDDMIRP + +void TTSourceEngine::ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1) { + wchar_t buf[128]; + + swprintf(buf, sizeof(buf) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi", grandParent0, grandParent1, parent0, parent1, child0, child1, cvt0, cvt1, this->fnBias + resIIPDMIRPFn); + this->Emit(buf); + + this->lastChild = invalidKnotNum; + + this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; // because don't know which branch taken + + this->round = rtg; + this->ttv[pv].dir = diagDir; this->ttv[pv].from = this->ttv[pv].to = invalidKnotNum; // branch may or may not have been taken + this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->usedpv = false; +} // TTSourceEngine::ResIIPDMIRP + +void TTSourceEngine::ASM(bool inLine, wchar_t text[]) { + this->Emit(text); + if (inLine) this->InitTTEngineState(false); +} // TTSourceEngine::ASM + +void TTSourceEngine::INSTCTRL(short fromPpem, short toPpem) { + wchar_t code[512]; + + // GETINFO 6 below corresponding to ROTATEDINTERPRETERQUERY | STRETCHEDINTERPRETERQUERY, cf. Interp.c + // turn off instructions if outside ppem range or rasterizer version >= 1.8 and (rotated or stretched) + swprintf(code, sizeof(code) / sizeof(wchar_t), + L"#PUSHOFF" BRK + L"MPPEM[]" BRK + L"#PUSH, %hi" BRK + L"GT[]" BRK + L"MPPEM[]" BRK + L"#PUSH, %hi" BRK + L"LT[]" BRK + L"OR[]" BRK + L"#PUSH, 1" BRK + L"GETINFO[]" BRK + L"#PUSH, 37" BRK + L"GTEQ[]" BRK + L"#PUSH, 1" BRK + L"GETINFO[]" BRK + L"#PUSH, 64" BRK + L"LTEQ[]" BRK + L"AND[]" BRK + L"#PUSH, 6" BRK + L"GETINFO[]" BRK + L"#PUSH, 0" BRK + L"NEQ[]" BRK + L"AND[]" BRK + L"OR[]" BRK + L"IF[]" BRK + L" #PUSH, 1, 1" BRK + L" INSTCTRL[]" BRK + L"EIF[]" BRK + L"#PUSHON" BRK,toPpem,fromPpem); + + this->Emit(code); +} // TTSourceEngine::INSTCTRL + +void TTSourceEngine::SCANCTRL(short ctrl) { + wchar_t code[maxLineSize]; + + swprintf(code, sizeof(code) / sizeof(wchar_t), L"SCANCTRL[], %hi" BRK, ctrl); + this->Emit(code); +} // TTSourceEngine::SCANCTRL + +void TTSourceEngine::SCANTYPE(short type) { + int32_t pos; + wchar_t code[maxLineSize]; + + pos = type <= 4 ? 0 : swprintf(code, sizeof(code)/sizeof(wchar_t), L"SCANTYPE[], %hi" BRK,type-4); // Mac rasterizer doesn't handle types > 4 + swprintf(&code[pos], sizeof(code) / sizeof(wchar_t), L"SCANTYPE[], %hi" BRK, type); + this->Emit(code); +} // TTSourceEngine::SCANTYPE + +void TTSourceEngine::SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]) { + short cvtCutIn; + wchar_t code[maxLineSize]; + + if(this->legacyCompile) + { + swprintf(code, sizeof(code) / sizeof(wchar_t), L"WS[], 22, 1 /* s[22] = diagonal control: on (by default) */"); + this->Emit(code); + } + + if (numCvtCutIns > 0) { + swprintf(code, sizeof(code) / sizeof(wchar_t), L"SCVTCI[], %i", cvtCutInValue[0]); + this->Emit(code); + if (!this->legacyCompile) + { + swprintf(code, sizeof(code) / sizeof(wchar_t), L"WS[], %i, %i", cvtCutInStorage, cvtCutInValue[0]); + this->Emit(code); + } + } + if (numCvtCutIns > 1) { + this->Emit(L"#PUSHOFF"); + cvtCutIn = 1; + while (cvtCutIn < numCvtCutIns) { + + if (!this->legacyCompile) + { + + swprintf(code, sizeof(code) / sizeof(wchar_t), + L"MPPEM[]" BRK + L"#PUSH, %hi" BRK + L"GTEQ[]" BRK + L"IF[]" BRK + L"#PUSH, %i, %i, %i" BRK + L"SCVTCI[]" BRK + L"WS[]", cvtCutInPpemSize[cvtCutIn], cvtCutInStorage, cvtCutInValue[cvtCutIn], cvtCutInValue[cvtCutIn]); + } + else + { + swprintf(code, sizeof(code) / sizeof(wchar_t), + L"MPPEM[]" BRK + L"#PUSH, %hi" BRK + L"GTEQ[]" BRK + L"IF[]" BRK + L"#PUSH, %i" BRK + L"SCVTCI[]",cvtCutInPpemSize[cvtCutIn],cvtCutInValue[cvtCutIn]); + } + + this->Emit(code); + cvtCutIn++; + if (this->legacyCompile && cvtCutIn == numCvtCutIns) { + swprintf(code, sizeof(code) / sizeof(wchar_t), + L"#PUSH, 22, 0" BRK + L"WS[] /* s[22] = diagonal control: off (by now) */"); this->Emit(code); + } + this->Emit( L"EIF[]"); + } + this->Emit(L"#PUSHON"); + this->Emit(L""); + } +} // TTSourceEngine::SCVTCI + +void TTSourceEngine::SetAspectRatioFlag(void) { + wchar_t code[maxLineSize]; + + this->Emit(L"/* Square aspect ratio? */"); + swprintf(code, sizeof(code) / sizeof(wchar_t), L"CALL[], %hi", this->fnBias + aspectRatioFn); + this->Emit(code); + this->Emit(L""); + this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv] = this->ttv[fv]; this->ttv[pv].dir = yRomanDir; + this->usedpv = false; +} // TTSourceEngine::SetAspectRatioFlag + +void TTSourceEngine::SetGreyScalingFlag(void) { + wchar_t code[maxLineSize]; + + this->Emit(L"/* Grey scaling? */"); + swprintf(code, sizeof(code) / sizeof(wchar_t), L"CALL[], %hi", this->fnBias + greyScalingFn); + this->Emit(code); + this->Emit(L""); +} // TTSourceEngine::SetGreyScalingFlag + +void TTSourceEngine::SetClearTypeCtrl(short ctrl) { + wchar_t code[maxLineSize]; + + if (ctrl > 0) { + swprintf(code, sizeof(code) / sizeof(wchar_t), + L"#PUSHOFF" BRK + L"#PUSH, 2, 2" BRK + L"RS[]" BRK + L"LTEQ[]" BRK + L"IF[]" BRK + L" #PUSH, %i, 3" BRK + L" INSTCTRL[]" BRK + L"EIF[]" BRK + L"#PUSHON" BRK,4); + } else { + swprintf(code,sizeof(code)/sizeof(wchar_t), L"/* (font not tuned for ClearType) */" BRK); + } + this->Emit(code); +} // TTSourceEngine::SetClearTypeCtrl + +void TTSourceEngine::CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum) { + wchar_t code[maxLineSize]; + + if (relative) { + swprintf(code, sizeof(code) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi", cvtNum, breakPpemSize, parentCvtNum, this->fnBias + doubleHeightFn); + this->sRound = false; this->round = rnone; // don't know which branch was taken... + } else { + swprintf(code, sizeof(code) / sizeof(wchar_t), L"CALL[], %hi, %hi, %hi, %hi", cvtNum, parentCvtNum, breakPpemSize, this->fnBias + singleWeightFn); + } + this->Emit(code); +} // TTSourceEngine::CvtRegularization + +void TTSourceEngine::ResetPrepState(void) { + wchar_t code[16]; + + if (this->round != rtg) { + this->sRound = false; this->round = rtg; + this->Emit(L"RTG[]"); + } + if (this->deltaShift != defaultDeltaShift) { + this->deltaShift = defaultDeltaShift; + swprintf(code, sizeof(code) / sizeof(wchar_t), L"SDS[], %hi", this->deltaShift); + this->Emit(code); + } + if (this->deltaBase != defaultDeltaBase) { + this->deltaBase = defaultDeltaBase; + swprintf(code, sizeof(code) / sizeof(wchar_t), L"SDB[], %hi", this->deltaBase); + this->Emit(code); + } +} // TTSourceEngine::ResetPrepState + +void TTSourceEngine::SetFunctionNumberBias(short bias) { + this->fnBias = Max(0,bias); +} // TTSourceEngine::SetFunctionNumberBias + +short TTSourceEngine::GetFunctionNumberBias(void) { + return this->fnBias; +} // TTSourceEngine::GetFunctionNumberBias + +void TTSourceEngine::InitTTEngine(bool legacyCompile, bool *memError) { + this->error = false; + + this->InitTTEngineState(true); + this->fnBias = 0; + this->bufPos = 0; + this->bufLen = minBufLen; + this->buf = (wchar_t*)NewP(this->bufLen * sizeof(wchar_t)); + this->error = this->buf == NULL; + if (!this->error) this->buf[this->bufPos] = 0; // add trailing 0 assuming minBufLen > 0 + this->mov[false] = L'm'; this->min[false] = L'<'; this->rnd[false] = L'r'; + this->mov[true] = L'M'; this->min[true] = L'>'; this->rnd[true] = L'R'; + this->col[linkAnyColor][0] = L' '; this->col[linkGrey][0] = L'G'; this->col[linkBlack][0] = L'B'; this->col[linkWhite][0] = L'W'; + this->col[linkAnyColor][1] = L' '; this->col[linkGrey][1] = L'r'; this->col[linkBlack][1] = L'l'; this->col[linkWhite][1] = L'h'; + this->legacyCompile = legacyCompile; + *memError = this->error; +} // TTSourceEngine::InitTTEngine + +void TTSourceEngine::TermTTEngine(TextBuffer *glyfText, bool *memError) { + if (!this->error && glyfText) { + glyfText->Delete(0,glyfText->TheLength()); + glyfText->AppendRange(this->buf,0,this->bufPos); + } + if (this->buf != NULL) DisposeP((void**)&this->buf); + *memError = this->error; +} // TTSourceEngine::TermTTEngine + +TTSourceEngine::TTSourceEngine(void) { /* nix */ } /* TTSourceEngine::TTSourceEngine */ +TTSourceEngine::~TTSourceEngine(void) { /* nix */ } /* TTSourceEngine::~TTSourceEngine */ + +void TTSourceEngine::InitTTEngineState(bool forNewGlyph) { + if (forNewGlyph) { // at start of glyf program, assume TrueType engine's defaults + this->rp[0] = this->rp[1] = this->rp[2] = 0; + this->minDist = one6; + this->sRound = false; + this->round = rtg; + this->ttv[fv].dir = xRomanDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv] = this->ttv[fv]; + this->usedpv = false; + this->autoFlip = true; + this->deltaBase = defaultDeltaBase; this->deltaShift = defaultDeltaShift; + this->lastChild = invalidKnotNum; + } else { // after Call or Inline, assume any kind of side-effect + this->rp[0] = this->rp[1] = this->rp[2] = invalidKnotNum; + this->minDist = -1; + this->sRound = false; + this->round = rnone; + this->ttv[fv].dir = diagDir; this->ttv[fv].from = this->ttv[fv].to = invalidKnotNum; + this->ttv[pv] = this->ttv[fv]; + this->usedpv = false; // ? + this->autoFlip = (bool)-1; + this->deltaBase = -48; this->deltaShift = -1; + this->lastChild = invalidKnotNum; + } +} // TTSourceEngine::InitTTEngineState + + +void GenGuardCond(TextBuffer *text, AltCodePath path) { + wchar_t codePath[32]; + + path = (AltCodePath)Min(Max(firstAltCodePath,path),lastAltCodePath); + + swprintf(codePath, sizeof(codePath)/sizeof(wchar_t), L"#PUSH, %i, 2",path); text->AppendLine(codePath); + text->AppendLine(L"RS[]"); + swprintf(codePath, sizeof(codePath) / sizeof(wchar_t),WIDE_STR_FORMAT L"EQ[]", path < altCodePathMonochromeOnly ? L"N" : L"LT"); + text->AppendLine(codePath); +} // GenGuardCond + +void GenTalkIf(TextBuffer *talk, AltCodePath path, int32_t fpgmBias) { + wchar_t codePath[32]; + + talk->AppendLine(L"Inline(\x22"); + talk->AppendLine(L"#PUSHOFF"); + GenGuardCond(talk,path); + talk->AppendLine(L"IF[]"); + talk->AppendLine(L"#PUSHON"); + talk->AppendLine(L"#BEGIN"); + talk->AppendLine(L"\x22)"); + talk->AppendLine(L""); + swprintf(codePath, sizeof(codePath) / sizeof(wchar_t), L"BeginCodePath(%i)", fpgmBias); + talk->AppendLine(codePath); + talk->AppendLine(L""); +} // GenTalkIf + +void GenTalkElse(TextBuffer *talk, int32_t fpgmBias) { + wchar_t codePath[32]; + + talk->AppendLine(L""); + talk->AppendLine(L"EndCodePath()"); + talk->AppendLine(L""); + talk->AppendLine(L"Inline(\x22"); + talk->AppendLine(L"#END"); + talk->AppendLine(L"ELSE[]"); + talk->AppendLine(L"#BEGIN"); + talk->AppendLine(L"\x22)"); + talk->AppendLine(L""); + swprintf(codePath, sizeof(codePath) / sizeof(wchar_t), L"BeginCodePath(%i)", fpgmBias); + talk->AppendLine(codePath); + talk->AppendLine(L""); +} // GenTalkElse + +void GenTalkEndIf(TextBuffer *talk) { + talk->AppendLine(L""); + talk->AppendLine(L"EndCodePath()"); + talk->AppendLine(L""); + talk->AppendLine(L"Inline(\x22"); + talk->AppendLine(L"#END"); + talk->AppendLine(L"EIF[]"); + talk->AppendLine(L"\x22)"); +} // GenTalkEndIf + +void GenPrepIf(TextBuffer *prep, AltCodePath path) { + wchar_t codePath[32]; + + prep->AppendLine(L"#PUSHOFF"); + swprintf(codePath, sizeof(codePath) / sizeof(wchar_t), L"#PUSH, %i", greyScalingFn); + prep->AppendLine(codePath); + prep->AppendLine(L"CALL[]"); + GenGuardCond(prep,path); + prep->AppendLine(L"IF[]"); + prep->AppendLine(L"#PUSHON"); + prep->AppendLine(L"#BEGIN"); + prep->AppendLine(L""); +} // GenPrepIf + +void GenPrepElse(TextBuffer *prep) { + prep->AppendLine(L""); + prep->AppendLine(L"#END"); + prep->AppendLine(L"ELSE[]"); + prep->AppendLine(L"#BEGIN"); + prep->AppendLine(L""); +} // GenPrepElse + +void GenPrepEndIf(TextBuffer *prep) { + prep->AppendLine(L""); + prep->AppendLine(L"#END"); + prep->AppendLine(L"EIF[]"); +} // GenPrepEndIf + + +TTEngine *NewTTSourceEngine(void) { + return new TTSourceEngine; +} diff --git a/src/TTEngine.h b/src/TTEngine.h index 9fcaafc..29282f1 100644 --- a/src/TTEngine.h +++ b/src/TTEngine.h @@ -1,139 +1,139 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef TTEngine_dot_h -#define TTEngine_dot_h - -#include "MathUtils.h" // Vector etc. -#include "GUIDecorations.h" // DeltaColor - -#define FIRSTTMPCVT 40 -#define SECONDTMPCVT (FIRSTTMPCVT + 1) // used by function 18 and TTSourceEngine::MIRP - -#define maxParams 64L -#define maxPpemSize 256L -#define maxMinDist 2L /* implementation restriction of TTEngine::AssertMinDist */ - -#define stdDeltaShift 3 // corresponding to stdDeltaResolution below -#define stdDeltaResolution (one6 >> stdDeltaShift) // 1/8th of a pixel -#define stdDeltaBase 9 - -#define maxPixelValue 8L*one6 /* corresponding to a magnitude 8 and deltaShift 1/2^0 */ - -#define maxAsmSize 0x2000L - -#define invalidKnotNum (-1) - -typedef enum {rthg = 0, rtdg, rtg, rdtg, rutg, roff, rnone} Rounding; - -typedef enum {xRomanDir = 0, yRomanDir, xItalDir, yItalDir, xAdjItalDir, yAdjItalDir, diagDir, perpDiagDir} TTVDirection; // ordered -#define firstTTVDirection xRomanDir -#define lastTTVDirection perpDiagDir -#define numTTVDirections (lastTTVDirection - firstTTVDirection + 1) -// xItalDir and xAdjItalDir correspond to the respective y directions minus 90 degrees, diagDir and perpDiagDir are for arbitrary S(FV|PV)TLs - -typedef enum {fv = 0, pv, dpv} TTVector; - -typedef struct { - TTVDirection dir; - short from,to; -} TTVectorDesc; - -DeltaColor DeltaColorOfByte(unsigned char byte); -DeltaColor DeltaColorOfOptions(bool grayScale, bool clearType, bool clearTypeCompWidth /* , bool clearTypeVertRGB */, bool clearTypeBGR, bool clearTypeFract, bool clearTypeYAA, bool clearTypeGray); -unsigned char ByteOfDeltaColor(DeltaColor color); -char *AllDeltaColorBytes(void); - -typedef enum { noAltCodePath = -1, altCodePathBWOnly = 0, altCodePathGreyOnly = 1, altCodePathMonochromeOnly = 2} AltCodePath; -#define firstAltCodePath altCodePathBWOnly -#define lastAltCodePath altCodePathMonochromeOnly -#define numAltCodePaths (lastAltCodePath - firstAltCodePath + 1) - -class TTEngine { -public: - virtual void Emit(const wchar_t text[]); - virtual void AssertFreeProjVector(TTVDirection dir); - virtual void AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot); - virtual void AssertFVonCA(bool y); - virtual void AssertPVonCA(bool y); - virtual void AssertFVonPV(void); - virtual TTVDirection FVDir(void); - virtual TTVDirection PVDir(void); - virtual void AssertRefPoint(short rp, short knot); - virtual void AssertRefPointPair(short rp0, short rp1, short knot0, short knot1); // not necessarily in that order - virtual short AssertEitherRefPointOnKnot(short rp0, short rp1, short knot); // returns the ref point actually asserted - virtual short AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp); // returns the knot actually asserted - virtual void AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]); - virtual void AssertAutoFlip(bool on); - virtual void AssertRounding(Rounding round); - virtual void AssertRoundingBelowPpem(Rounding round, short ppem); - virtual void AssertSuperRounding(short period, short phase, short thresHold); - virtual void RoundDownToGridBelowPpem(short ppem); - virtual void IfPpemBelow(short ppem); - virtual void Else(void); - virtual void End(bool invalidateRefPoints); - virtual void MDAP(bool round, short knot); - virtual void MIAP(bool round, short knot, short cvt); - virtual void MDRP(bool minDist, bool round, short color, short knot); - virtual void MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative); - virtual void DMIRP(short knot, short cvt, short pvFrom, short pvTo); - virtual void ALIGNRP(short knot); - virtual void IP(short knots, short knot[]); - virtual void SHP(short rp, short knots, short knot[]); - virtual void SHPIX(short knots, short knot[], F26Dot6 amount); - virtual void SLOOP(short count); - virtual void ISECT(short intersection, short line0start, short line0end, short line1start, short line1end); - virtual void IUP(bool y); - virtual void IPRange(bool y, short parent0, short parent1, short start, short end); - virtual void DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]); - virtual void CALL24(short leftCvt, short rightCvt); - virtual void CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1); // upright serifs with horizontal base - virtual void CALL64(short parent, short child, short cvt, bool half, bool flip); // "special MIRP" for new italic strokes - virtual void CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip); // new diagonal stroke - virtual void CALL678(bool back, short knot, short sameSide, short cvt, short storage); // new italic strokes: extrapolate knot to cvt height or back again - virtual void CALL012345(short type, short knot0, short knot1, short knot2, short cvt); // cf. fdefs.0 - virtual void CALL6(short knots, short knot[], short targetKnot); // cf. fdefs.0 - virtual void CALL378(short type, short targetKnot); - virtual void CALL88(short riseCvt, short runCvt); - virtual void ResMIAP(short child, short cvt); - virtual void ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1); - virtual void ResMIRP(short parent, short child, short cvt, bool useMinDist); - virtual void ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1); - virtual void ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1); - virtual void ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); - virtual void ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1); - virtual void ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); - virtual void CALL(short actParams, short anyNum[], short functNum); - virtual void ASM(bool inLine, wchar_t text[]); - virtual void INSTCTRL(short fromPpem, short toPpem); - virtual void SCANCTRL(short ctrl); - virtual void SCANTYPE(short type); - virtual void SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]); - virtual void SetAspectRatioFlag(void); - virtual void SetGreyScalingFlag(void); - virtual void SetClearTypeCtrl(short ctrl); - virtual void CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum); - virtual void ResetPrepState(void); - virtual void SetFunctionNumberBias(short bias); - virtual short GetFunctionNumberBias(void); - virtual void InitTTEngine(bool legacyCompile, bool *memError); - virtual void TermTTEngine(TextBuffer *glyfText, bool *memError); - TTEngine(void); - virtual ~TTEngine(void); -}; - - -void GenTalkIf(TextBuffer *talk, AltCodePath path, int32_t fpgmBias); -void GenTalkElse(TextBuffer *talk, int32_t fpgmBias); -void GenTalkEndIf(TextBuffer *talk); -void GenPrepIf(TextBuffer *prep, AltCodePath path); -void GenPrepElse(TextBuffer *prep); -void GenPrepEndIf(TextBuffer *prep); - -TTEngine *NewTTSourceEngine(void); - -/***** as a matter of centralization, it might be cleaner to integrate (most of) fdefs0|1|2.c -into (an extended version of) TTEngine, to properly encapsulate the graphic state and whatever -side-effect updates required as a result of a function call... *****/ - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef TTEngine_dot_h +#define TTEngine_dot_h + +#include "MathUtils.h" // Vector etc. +#include "GUIDecorations.h" // DeltaColor + +#define FIRSTTMPCVT 40 +#define SECONDTMPCVT (FIRSTTMPCVT + 1) // used by function 18 and TTSourceEngine::MIRP + +#define maxParams 64L +#define maxPpemSize 256L +#define maxMinDist 2L /* implementation restriction of TTEngine::AssertMinDist */ + +#define stdDeltaShift 3 // corresponding to stdDeltaResolution below +#define stdDeltaResolution (one6 >> stdDeltaShift) // 1/8th of a pixel +#define stdDeltaBase 9 + +#define maxPixelValue 8L*one6 /* corresponding to a magnitude 8 and deltaShift 1/2^0 */ + +#define maxAsmSize 0x2000L + +#define invalidKnotNum (-1) + +typedef enum {rthg = 0, rtdg, rtg, rdtg, rutg, roff, rnone} Rounding; + +typedef enum {xRomanDir = 0, yRomanDir, xItalDir, yItalDir, xAdjItalDir, yAdjItalDir, diagDir, perpDiagDir} TTVDirection; // ordered +#define firstTTVDirection xRomanDir +#define lastTTVDirection perpDiagDir +#define numTTVDirections (lastTTVDirection - firstTTVDirection + 1) +// xItalDir and xAdjItalDir correspond to the respective y directions minus 90 degrees, diagDir and perpDiagDir are for arbitrary S(FV|PV)TLs + +typedef enum {fv = 0, pv, dpv} TTVector; + +typedef struct { + TTVDirection dir; + short from,to; +} TTVectorDesc; + +DeltaColor DeltaColorOfByte(unsigned char byte); +DeltaColor DeltaColorOfOptions(bool grayScale, bool clearType, bool clearTypeCompWidth /* , bool clearTypeVertRGB */, bool clearTypeBGR, bool clearTypeFract, bool clearTypeYAA, bool clearTypeGray); +unsigned char ByteOfDeltaColor(DeltaColor color); +char *AllDeltaColorBytes(void); + +typedef enum { noAltCodePath = -1, altCodePathBWOnly = 0, altCodePathGreyOnly = 1, altCodePathMonochromeOnly = 2} AltCodePath; +#define firstAltCodePath altCodePathBWOnly +#define lastAltCodePath altCodePathMonochromeOnly +#define numAltCodePaths (lastAltCodePath - firstAltCodePath + 1) + +class TTEngine { +public: + virtual void Emit(const wchar_t text[]); + virtual void AssertFreeProjVector(TTVDirection dir); + virtual void AssertTTVonLine(TTVector ttv, short parent0, short parent1, Vector P0, Vector P1, bool rot); + virtual void AssertFVonCA(bool y); + virtual void AssertPVonCA(bool y); + virtual void AssertFVonPV(void); + virtual TTVDirection FVDir(void); + virtual TTVDirection PVDir(void); + virtual void AssertRefPoint(short rp, short knot); + virtual void AssertRefPointPair(short rp0, short rp1, short knot0, short knot1); // not necessarily in that order + virtual short AssertEitherRefPointOnKnot(short rp0, short rp1, short knot); // returns the ref point actually asserted + virtual short AssertEitherKnotOnRefPoint(short knot0, short knot1, short rp); // returns the knot actually asserted + virtual void AssertMinDist(short minDists, short jumpPpemSize[], F26Dot6 pixelSize[]); + virtual void AssertAutoFlip(bool on); + virtual void AssertRounding(Rounding round); + virtual void AssertRoundingBelowPpem(Rounding round, short ppem); + virtual void AssertSuperRounding(short period, short phase, short thresHold); + virtual void RoundDownToGridBelowPpem(short ppem); + virtual void IfPpemBelow(short ppem); + virtual void Else(void); + virtual void End(bool invalidateRefPoints); + virtual void MDAP(bool round, short knot); + virtual void MIAP(bool round, short knot, short cvt); + virtual void MDRP(bool minDist, bool round, short color, short knot); + virtual void MIRP(bool minDist, bool round, short color, short knot, short cvt, bool negative); + virtual void DMIRP(short knot, short cvt, short pvFrom, short pvTo); + virtual void ALIGNRP(short knot); + virtual void IP(short knots, short knot[]); + virtual void SHP(short rp, short knots, short knot[]); + virtual void SHPIX(short knots, short knot[], F26Dot6 amount); + virtual void SLOOP(short count); + virtual void ISECT(short intersection, short line0start, short line0end, short line1start, short line1end); + virtual void IUP(bool y); + virtual void IPRange(bool y, short parent0, short parent1, short start, short end); + virtual void DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[]); + virtual void CALL24(short leftCvt, short rightCvt); + virtual void CALL3456(short type, short knot3, short cvt3, short knot2, short cvt2, short knot1, short cvt1); // upright serifs with horizontal base + virtual void CALL64(short parent, short child, short cvt, bool half, bool flip); // "special MIRP" for new italic strokes + virtual void CALL656(bool crissCrossLinks, short knot0, short knot1, short knot2, short knot3, short cvt, short storage, bool xLinks, bool flip); // new diagonal stroke + virtual void CALL678(bool back, short knot, short sameSide, short cvt, short storage); // new italic strokes: extrapolate knot to cvt height or back again + virtual void CALL012345(short type, short knot0, short knot1, short knot2, short cvt); // cf. fdefs.0 + virtual void CALL6(short knots, short knot[], short targetKnot); // cf. fdefs.0 + virtual void CALL378(short type, short targetKnot); + virtual void CALL88(short riseCvt, short runCvt); + virtual void ResMIAP(short child, short cvt); + virtual void ResIPMDAP(TTVDirection pvP, bool postRoundFlag, short parent0, short child, short parent1); + virtual void ResMIRP(short parent, short child, short cvt, bool useMinDist); + virtual void ResIPMIRP(TTVDirection pvGP, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1); + virtual void ResDDMIRP(short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1); + virtual void ResIPDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); + virtual void ResIPDDMIRP(TTVDirection pvGP, short grandParent0, short parent0, short child0, TTVectorDesc fv0, short cvt0, short parent1, short child1, TTVectorDesc fv1, short cvt1, short grandParent1); + virtual void ResIIPDMIRP(short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1); + virtual void CALL(short actParams, short anyNum[], short functNum); + virtual void ASM(bool inLine, wchar_t text[]); + virtual void INSTCTRL(short fromPpem, short toPpem); + virtual void SCANCTRL(short ctrl); + virtual void SCANTYPE(short type); + virtual void SCVTCI(short numCvtCutIns, short cvtCutInPpemSize[], F26Dot6 cvtCutInValue[]); + virtual void SetAspectRatioFlag(void); + virtual void SetGreyScalingFlag(void); + virtual void SetClearTypeCtrl(short ctrl); + virtual void CvtRegularization(bool relative, short cvtNum, short breakPpemSize, short parentCvtNum); + virtual void ResetPrepState(void); + virtual void SetFunctionNumberBias(short bias); + virtual short GetFunctionNumberBias(void); + virtual void InitTTEngine(bool legacyCompile, bool *memError); + virtual void TermTTEngine(TextBuffer *glyfText, bool *memError); + TTEngine(void); + virtual ~TTEngine(void); +}; + + +void GenTalkIf(TextBuffer *talk, AltCodePath path, int32_t fpgmBias); +void GenTalkElse(TextBuffer *talk, int32_t fpgmBias); +void GenTalkEndIf(TextBuffer *talk); +void GenPrepIf(TextBuffer *prep, AltCodePath path); +void GenPrepElse(TextBuffer *prep); +void GenPrepEndIf(TextBuffer *prep); + +TTEngine *NewTTSourceEngine(void); + +/***** as a matter of centralization, it might be cleaner to integrate (most of) fdefs0|1|2.c +into (an extended version of) TTEngine, to properly encapsulate the graphic state and whatever +side-effect updates required as a result of a function call... *****/ + #endif // TTEngine_dot_h \ No newline at end of file diff --git a/src/TTFont.cpp b/src/TTFont.cpp index eb9b018..086eed8 100644 --- a/src/TTFont.cpp +++ b/src/TTFont.cpp @@ -1,4993 +1,5043 @@ -/// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include // assert -#include -#include -#include "opentypedefs.h" // for SWAP etc. -#include "pch.h" -#include "CvtManager.h" -#include "TTAssembler.h" -#include "TTFont.h" - -#define PRIVATE_GLIT1 'TSI0' /* editor 0 */ -#define PRIVATE_PGM1 'TSI1' /* editor 1 */ -#define PRIVATE_GLIT2 'TSI2' /* editor 2 */ -#define PRIVATE_PGM2 'TSI3' /* editor 3 */ -#define PRIVATE_CMAP 'TSI4' /* Copy of Cmap */ -#define PRIVATE_GROUP 'TSI5' /* group type for each glyph : uppercase, lowercase,... */ - -#define PRIVATE_GSUB 'TSIS' -#define PRIVATE_GPOS 'TSIP' -#define PRIVATE_GDEF 'TSID' -#define PRIVATE_BASE 'TSIB' -#define PRIVATE_JSTF 'TSIJ' - -#define PRIVATE_CVAR 'TSIC' - -#define PRIVATE_STAMP_1 0xABFC1F34 /* fonts stored by char codes... */ -#define PRIVATE_STAMP_2 0xCA0A2117 /* fonts stored by glyph index... */ - -#define GLIT_PAD 5 -#define PRE_PGM_GLYPH_INDEX 0xFFFA -#define CVT_GLYPH_INDEX 0xFFFB -#define HL_GLYPH_INDEX 0xFFFC -#define FONT_PGM_GLYPH_INDEX 0xFFFD -#define PRIVATE_STAMP_INDEX 0xFFFE -static const unsigned short glitPadIndex[GLIT_PAD] = {PRIVATE_STAMP_INDEX, PRE_PGM_GLYPH_INDEX, FONT_PGM_GLYPH_INDEX, CVT_GLYPH_INDEX, HL_GLYPH_INDEX}; - -#define ABSOLUTE_MAX_GLYPHS 0xFFFF // can't have more than 2^16-1 glyphs, unicode 0xffff reserved to indicate "byGlyphIndex..." - -// notice that these macros won't convert big endians into little endians for the windows platform... -#define READALIGNEDWORD( p ) ( p += 2, *(short *)(p-2) ) -#define READNONALIGNEDWORD( p ) ( p += 2, (short)( (((short)*(p-2)) << 8) | *(unsigned char *)(p-1) ) ) -#define COPYALIGNEDWORD(d, s) (s += 2, d += 2, *(short *)(d-2) = *(short *)(s-2)) -#define WRITEALIGNEDWORD( p, x ) ( *((short *)p) = (x), p += 2 ) -#define WRITENONALIGNEDWORD( p, x ) ( *p++ = (unsigned char) ((x) >> 8), *p++ = (unsigned char) (x) ) - -#define DWordPad(x) ((x + 3) & ~3) - -#define OnReserve(x,maxX) ((x)*5 >= (maxX)*4) // reached 80% = 4/5 capacity -#define AddReserve(x) ((x) + (x)/4) - -// character group codes as encoded in a TrueType file; notice that this is not the same encoding as used in the "cvt comment"... -#define TOTALTYPE 8 /* total number of type groups */ -#define CG_UNKNOWN 0 -#define CG_OTHER 1 -#define CG_UPPERCASE 2 -#define CG_LOWERCASE 3 -#define CG_FIGURE 4 -#define CG_PUNCTUATION 5 -#define CG_MS_BOXES 6 -#define CG_DINGBATS 7 - -#define OFFSETIndex 0 -#define SOFFSETIndex 1 -#define ANCHORIndex 2 -#define SANCHORIndex 3 -#define OVERLAPIndex 4 -#define NONOVERLAPIndex 5 -#define USEMYMETRICSIndex 6 -#define SCALEDCOMPONENTOFFSETIndex 7 -#define UNSCALEDCOMPONENTOFFSETIndex 8 - -#define MYOFFSETTABLESIZE 1000 - -// https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats -#define POINTS_ARE_WORDS 0x80 // Flag indicating the data type used for point numbers in this run. If set, the point numbers are stored as unsigned 16-bit values (uint16); if clear, the point numbers are stored as unsigned bytes (uint8). -#define POINT_RUN_COUNT_MASK 0x7F // Mask for the low 7 bits of the control byte to give the number of point number elements, minus 1. - -#define DELTAS_ARE_ZERO 0x80 // Flag indicating that this run contains no data (no explicit delta values are stored), and that the deltas for this run are all zero. -#define DELTAS_ARE_WORDS 0x40 // Flag indicating the data type for delta values in the run. If set, the run contains 16-bit signed deltas (int16); if clear, the run contains 8-bit signed deltas (int8). -#define DELTA_RUN_COUNT_MASK 0x3F // Mask for the low 6 bits to provide the number of delta values in the run, minus one. - -const short charGroupToIntInFile[TOTALTYPE] = {CG_UNKNOWN, CG_OTHER, CG_UPPERCASE, CG_LOWERCASE, CG_FIGURE, CG_UNKNOWN, CG_UNKNOWN, CG_UNKNOWN}; - -const CharGroup intInFileToCharGroup[TOTALTYPE] = {anyGroup, otherCase, upperCase, lowerCase, figureCase, otherCase, otherCase, otherCase}; // map CG_MS_BOXES to CG_OTHER as per elik - -typedef struct { - unsigned char bCharSet; // 0 = no subset; 1 = win ansi - unsigned char xRatio; - unsigned char yStartRatio; - unsigned char yEndRatio; -} VDMXRatio; - -typedef struct { - unsigned short ppemHeight; - short yMax; - short yMin; -} VDMXEntry; - -typedef struct { - unsigned short numEntries; - unsigned char startPpemHeight; - unsigned char endPpemHeight; - VDMXEntry entry[1]; -} VDMXGroup; - -typedef struct { - unsigned short version; - unsigned short numGroups; - unsigned short numRatios; - VDMXRatio ratio[1]; -} VDMXTable; - -typedef struct { - unsigned int glyphId; - const char * glyphName; -} PSNAMELIST; - -TrueTypeGlyph::TrueTypeGlyph(void) { -} // TrueTypeGlyph::TrueTypeGlyph - -TrueTypeGlyph::~TrueTypeGlyph(void) { - // nix -} // TrueTypeGlyph::~TrueTypeGlyph - -int32_t ColorTransition(Vector V[], Vector W[]); -int32_t ColorTransition(Vector V[], Vector W[]) { // cf. also Intersect in "MathUtils.h" - int32_t a11,a12,a21,a22,b1,b2,det,t1,t2; - - b1 = V[0].x - W[1].x; a11 = V[0].x - V[1].x; a12 = W[2].x - W[1].x; - b2 = V[0].y - W[1].y; a21 = V[0].y - V[1].y; a22 = W[2].y - W[1].y; - det = a11*a22 - a12*a21; - if (det == 0) return 0; // parallel - t1 = a22*b1 - a12*b2; - t2 = a11*b2 - a21*b1; - return (Sgn(t1) == Sgn(det) && 0 < Abs(t1) && Abs(t1) < Abs(det) && // intersection point is inside line V - ((Sgn(t2) == Sgn(det) && 0 < Abs(t2) && Abs(t2) < Abs(det)) || // intersection point is inside line W - (t2 == det && Sgn(VectProdP(V[0],W[1],V[0],W[2]))*Sgn(VectProdP(V[0],W[3],V[0],W[2])) < 0))) ? 1 : 0; // or at the end of W and W arrives/departs on different sides of V -} // ColorTransition - -LinkColor TrueTypeGlyph::TheColor(short from, short to) { - Vector V[3],D[2],C[2],W[4]; - int32_t cont,pred,knot,succ,start,zero,end,n,parity,sgn,dir[4]; - - for (cont = 0; cont < this->numContoursInGlyph; cont++) { - start = this->startPoint[cont]; - n = this->endPoint[cont] - start + 1; - V[0].x = this->x[start+n-1]; V[1].x = this->x[start]; - V[0].y = this->y[start+n-1]; V[1].y = this->y[start]; - D[0] = SubV(V[1],V[0]); - for (knot = 0; knot < n; knot++) { - V[2].x = this->x[start + (knot + 1)%n]; - V[2].y = this->y[start + (knot + 1)%n]; - D[1] = SubV(V[2],V[1]); - sgn = D[0].x*D[1].y - D[0].y*D[1].x; - this->dirChange[start + knot] = (short)Sgn(sgn); // +1 => left turn, -1 => right turn, 0 => straight - V[0] = V[1]; V[1] = V[2]; D[0] = D[1]; - } - } - - V[0].x = this->x[from] << 1; - V[0].y = this->y[from] << 1; - V[1].x = this->x[to] << 1; - V[1].y = this->y[to] << 1; - C[0] = ShrV(AddV(V[0],V[1]),1); // use extra bit at end to avoid precision loss upon dividing - C[1].x = -32768; C[1].y = C[0].y-1; // and make sure color transition test line does not align with any straight line in the glyph... - parity = 0; - for (cont = 0; cont < this->numContoursInGlyph; cont++) { - start = this->startPoint[cont]; - end = this->endPoint[cont]; - n = end - start + 1; - for (pred = start + n - 1; pred >= start && !(dir[0] = this->dirChange[pred]); pred--); // treat aligned straight lines as one single straight line - for (zero = start; zero < start + n && !(dir[1] = this->dirChange[zero]); zero++); // treat aligned straight lines as one single straight line - if (zero < start + n) { // else pathological case of flat polygon - W[0].x = this->x[pred] << 1; - W[0].y = this->y[pred] << 1; - W[1].x = this->x[zero] << 1; - W[1].y = this->y[zero] << 1; - knot = zero; - do { - do knot = knot == end ? start : knot + 1; while (!(dir[2] = this->dirChange[knot])); - W[2].x = this->x[knot] << 1; - W[2].y = this->y[knot] << 1; - succ = knot; - do succ = succ == end ? start : succ + 1; while (!(dir[3] = this->dirChange[succ])); - W[3].x = this->x[succ] << 1; - W[3].y = this->y[succ] << 1; - if (Collinear(W[1],V[0],W[2],notOutside) && Collinear(W[1],V[1],W[2],notOutside)) - return dir[1] + dir[2] == 2 ? linkWhite : linkBlack; // V completely contained in W, concave edges are white, convex ones and stairs are black - else if (VectProdP(V[0],V[1],W[1],W[2]) == 0 && - (Collinear(W[1],V[0],W[2],inside) || Collinear(W[1],V[1],W[2],inside) || Collinear(V[0],W[1],V[1],inside) || Collinear(V[0],W[2],V[1],inside))) - return linkGrey; // V partly overlaps W or v.v. - else if (ColorTransition(V,W)) - return linkGrey; - parity += ColorTransition(C,W); // else we are still either all black or all white, so keep track of parity - W[0] = W[1]; W[1] = W[2]; dir[0] = dir[1]; dir[1] = dir[2]; - } while (knot != zero); - } - } - return parity & 1 ? linkBlack : linkWhite; -} // TrueTypeGlyph::TheColor - -TrueTypeFont::TrueTypeFont(void) { - int32_t i; - - this->sfntHandle = NULL; - this->sfntTmpHandle = NULL; - this->offsetTable = NULL; - this->tmpOffsetTable = NULL; - this->incrBuildSfntData = NULL; - this->cvt = NULL; - this->IndexToLoc = NULL; - this->tmpIndexToLoc = NULL; - this->glit1 = NULL; - this->glit2 = NULL; - this->charCodeOf = NULL; - this->charGroupOf = NULL; - this->glyphIndexMap = NULL; - for (i = 0; i < Len(this->binData); i++) { - this->binSize[i] = 0; - this->binData[i] = NULL; - } - this->devMetricsPtr = NULL; - this->ltshPtr = NULL; - this->vdmxPtr = NULL; - this->horMetric = NULL; - this->postScriptNames = NULL; - - this->bVariationTypeface_ = false; - this->axisCount_ = 0; -} // TrueTypeFont::TrueTypeFont - -bool TrueTypeFont::Create() { -// create this part of font only once to avoid memory fragmentation - this->maxSfntSize = trialSfntSize; - this->maxTmpSfntSize = trialSfntSize; - this->maxGlitEntries = trialNumGlyphs + 5; -# - this->sfntHandle = (unsigned char *)NewP(this->maxSfntSize); - this->sfntTmpHandle = (unsigned char *)NewP(this->maxSfntSize); - - this->offsetTable = (sfnt_OffsetTable *) NewP(MYOFFSETTABLESIZE); - this->tmpOffsetTable = (sfnt_OffsetTable *) NewP(MYOFFSETTABLESIZE); -// this->incrBuildSfntData allocated and deallocated locally - this->cvt = NewControlValueTable(); - this->IndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*this->maxGlitEntries); - this->tmpIndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*this->maxGlitEntries); - this->glit1 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*this->maxGlitEntries); - this->glit2 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*this->maxGlitEntries); - this->charCodeOf = (uint32_t *)NewP(sizeof(uint32_t)*this->maxGlitEntries); - this->charGroupOf = (unsigned char *)NewP(this->maxGlitEntries); - this->glyphIndexMap = new std::vector; - this->tmpFlags = (unsigned char *)NewP(MAXPOINTS); - this->hdmxBinSize = 0; - this->ltshBinSize = 0; - this->vdmxBinSize = 0; - this->gaspBinSize = 0; - this->tsicBinSize = 0; - this->cvarBinSize = 0; - this->horMetric = (sfnt_HorizontalMetrics *)NewP(sizeof(sfnt_HorizontalMetrics)*this->maxGlitEntries); - this->gaspLoaded = false; - - return this->sfntHandle && this->sfntTmpHandle && this->offsetTable && this->tmpOffsetTable && this->cvt && this->IndexToLoc && this->tmpIndexToLoc && - this->glit1 && this->glit2 && this->charCodeOf && this->charGroupOf && this->horMetric && this->glyphIndexMap; -} // TrueTypeFont::Create - -TrueTypeFont::~TrueTypeFont(void) { - int32_t i; - - if (this->horMetric) DisposeP((void **)&this->horMetric); - if (this->vdmxPtr) DisposeP((void **)&this->vdmxPtr); - if (this->ltshPtr) DisposeP((void **)&this->ltshPtr); - if (this->devMetricsPtr) DisposeP((void **)&this->devMetricsPtr); - if (this->tmpFlags) DisposeP((void **)&this->tmpFlags); - for (i = 0; i < Len(this->binData); i++) { - this->binSize[i] = 0; - if (this->binData[i] != NULL) DisposeP((void **)&this->binData[i]); - } - if (this->glyphIndexMap) { - delete this->glyphIndexMap; - this->glyphIndexMap = NULL; - } - if (this->charGroupOf) DisposeP((void **)&this->charGroupOf); - if (this->charCodeOf) DisposeP((void **)&this->charCodeOf); - if (this->glit2) DisposeP((void **)&this->glit2); - if (this->glit1) DisposeP((void **)&this->glit1); - if (this->tmpIndexToLoc) DisposeP((void **)&this->tmpIndexToLoc); - if (this->IndexToLoc) DisposeP((void **)&this->IndexToLoc); - if (this->cvt) delete this->cvt; -// this->incrBuildSfntData allocated and deallocated locally - if (this->tmpOffsetTable) DisposeP((void **)&this->tmpOffsetTable); - if (this->offsetTable) DisposeP((void **)&this->offsetTable); - - if (this->postScriptNames) { - delete this->postScriptNames; - this->postScriptNames = NULL; - } - - if (this->sfntTmpHandle) DisposeP((void **)&this->sfntTmpHandle); - if (this->sfntHandle) DisposeP((void **)&this->sfntHandle); - -} // TrueTypeFont::~TrueTypeFont - -void TrueTypeFont::AssertMaxSfntSize(uint32_t minSfntSize, bool assertMainHandle, bool assertTempHandle) { - bool enough; - - minSfntSize = AddReserve(((minSfntSize + 0xfffff) >> 20) << 20); // round up to the nearest MB and add reserve - - enough = true; // assume - if (assertMainHandle && minSfntSize > this->maxSfntSize) enough = false; - if (assertTempHandle && minSfntSize > this->maxTmpSfntSize) enough = false; - if (enough) return; // we're done, enough reserve - - if (assertTempHandle && this->sfntTmpHandle) DisposeP((void **)&this->sfntTmpHandle); - if (assertMainHandle && this->sfntHandle) DisposeP((void **)&this->sfntHandle); - - if (assertMainHandle) { - this->sfntHandle = (unsigned char *)NewP(minSfntSize); -# - if (this->sfntHandle) { - this->maxSfntSize = minSfntSize; - } else { - this->maxSfntSize = 0; // we've just de-allocated it and failed to allocate it - } - } - if (assertTempHandle) { - this->sfntTmpHandle = (unsigned char *)NewP(minSfntSize); - - if (this->sfntTmpHandle) { - this->maxTmpSfntSize = minSfntSize; - } else { - this->maxTmpSfntSize = 0; // we've just de-allocated it and failed to allocate it - } - } -} // TrueTypeFont::AssertMaxSfntSize - -void TrueTypeFont::AssertMaxGlyphs(int32_t minGlyphs) { - - if (AddReserve(minGlyphs) < this->maxGlitEntries) return; // we're done, enough reserve - - if (this->horMetric) DisposeP((void**)&this->horMetric); - if (this->charGroupOf) DisposeP((void**)&this->charGroupOf); - if (this->charCodeOf) DisposeP((void**)&this->charCodeOf); - if (this->glit2) DisposeP((void**)&this->glit2); - if (this->glit1) DisposeP((void**)&this->glit1); - if (this->tmpIndexToLoc) DisposeP((void**)&this->tmpIndexToLoc); - if (this->IndexToLoc) DisposeP((void**)&this->IndexToLoc); - - minGlyphs = AddReserve(((minGlyphs + 0x3ff) >> 10) << 10); // round up to the nearest k and add reserve - minGlyphs = Min(minGlyphs,ABSOLUTE_MAX_GLYPHS); // but don't push the limit - - this->IndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*minGlyphs); - this->tmpIndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*minGlyphs); - this->glit1 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*minGlyphs); - this->glit2 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*minGlyphs); - this->charCodeOf = (uint32_t *)NewP(sizeof(uint32_t)*minGlyphs); - this->charGroupOf = (unsigned char *)NewP(minGlyphs); - this->horMetric = (sfnt_HorizontalMetrics *)NewP(sizeof(sfnt_HorizontalMetrics)*minGlyphs); - - this->maxGlitEntries = this->IndexToLoc && this->tmpIndexToLoc && this->glit1 && this->glit2 && this->charCodeOf && this->charGroupOf && this->horMetric ? minGlyphs : 0; -} // TrueTypeFont::AssertMaxGlyphs - -void MaxSfntSizeError(const wchar_t from[], int32_t size, wchar_t errMsg[]); -void MaxSfntSizeError(const wchar_t from[], int32_t size, wchar_t errMsg[]) { - swprintf(errMsg,WIDE_STR_FORMAT L", \r" BULLET L" Unable to allocate %li to work on this font.",from, size); -} // MaxSfntSizeError - -bool TrueTypeFont::SetSfnt(short platformID, short encodingID, wchar_t errMsg[]) -{ - this->UnpackFvar(); - this->UnpackAvar(); - - cvtVariationInterpolator_ = std::make_shared(); - - if (instanceManager_ == nullptr) - { - instanceManager_ = std::make_shared(); - } - - variationAxisTags_ = std::make_shared>(); - - if (this->HasFvar()) - { - axisCount_ = fvar_.axisCount; - bVariationTypeface_ = true; - - // Set name table names per axis - for (size_t index = 0; index < axisCount_; index++) - { - uint32_t tag = fvar_.axisRecords[index].axisTag; - variationAxisTags_->insert(variationAxisTags_->end(), tag); - } - - instanceManager_->SetAxisCount(axisCount_); - - if (this->HasPrivateCvar()) - { - TSICHeader tsic; - this->tsicError = !this->GetPrivateCvar(tsic); - if (this->tsicError) - { - std::wstring str = L"Error: Unable to load TSIC table!" BRK; - return false; - } - else - { - this->MergePrivateCvarWithInstanceManager(tsic); - } - } - - instanceManager_->Sort(); - } - - return true; -} // SetSfnt - - void TrueTypeFont::UnpackFvar(void) -{ - unsigned char* charStart = this->GetTablePointer(tag_FontVariations); - unsigned short* start = reinterpret_cast(charStart); - if (start == nullptr) - return; - - fvar_.axisRecords.clear(); - fvar_.instanceRecords.clear(); - - unsigned short* packed = start; - - fvar_.majorVersion = SWAPWINC(packed); - fvar_.minorVersion = SWAPWINC(packed); - fvar_.offsetToAxesArray = SWAPWINC(packed); - fvar_.countSizePairs = SWAPWINC(packed); - fvar_.axisCount = SWAPWINC(packed); - fvar_.axisSize = SWAPWINC(packed); - fvar_.instanceCount = SWAPWINC(packed); - fvar_.instanceSize = SWAPWINC(packed); - - packed = (unsigned short*)(charStart + fvar_.offsetToAxesArray); - for (int16_t axisIndex = 0; axisIndex < fvar_.axisCount; axisIndex++) - { - VariationAxisRecord axisRecord; - unsigned short* packed1 = packed; - - uint32_t* ptag = reinterpret_cast(packed1); - axisRecord.axisTag = *ptag; - packed1 += 2; - - int32_t* pminValue = reinterpret_cast(packed1); - int32_t minValue = SWAPL(*pminValue); - axisRecord.minValue.SetRawValue(minValue); - packed1 += 2; - - // int32_t* pdefaultValue = reinterpret_cast(packed1); - int32_t defaultValue = SWAPL(*pminValue); - axisRecord.defaultValue.SetRawValue(defaultValue); - packed1 += 2; - - // int32_t* pmaxValue = reinterpret_cast(packed1); - int32_t maxValue = SWAPL(*pminValue); - axisRecord.maxValue.SetRawValue(maxValue); - packed1 += 2; - - axisRecord.flags = SWAPWINC(packed1); - axisRecord.nameID = SWAPWINC(packed1); - - fvar_.axisRecords.push_back(axisRecord); - - packed = reinterpret_cast(reinterpret_cast(packed) + fvar_.axisSize); - } - - for (uint16_t instanceIndex = 0; instanceIndex < fvar_.instanceCount; instanceIndex++) - { - VariationInstanceRecord instanceRecord; - unsigned short* packed1 = packed; - - instanceRecord.nameID = SWAPWINC(packed1); - instanceRecord.flags = SWAPWINC(packed1); - - for (int16_t axisIndex = 0; axisIndex < fvar_.axisCount; axisIndex++) - { - int32_t* pcoord = reinterpret_cast(packed1); - Fixed16_16 coord(SWAPL(*pcoord)); - packed1 += 2; - - instanceRecord.coordinates.push_back(coord); - } - - fvar_.instanceRecords.push_back(instanceRecord); - - // don't bother with optional postScriptNameID for now - - packed = reinterpret_cast(reinterpret_cast(packed) + fvar_.instanceSize); - } -} - -void TrueTypeFont::UnpackAvar(void) -{ - unsigned short* packed = reinterpret_cast(this->GetTablePointer(tag_AxisVariations)); - if (packed == nullptr) - return; - - avar_.axisSegmentMaps.clear(); - - avar_.majorVersion = SWAPWINC(packed); - avar_.minorVersion = SWAPWINC(packed); - avar_.reserved = SWAPWINC(packed); - avar_.axisCount = SWAPWINC(packed); - - // segment maps array - for (uint16_t axisIndex = 0; axisIndex < avar_.axisCount; ++axisIndex) - { - ShortFracSegment segment; - segment.pairCount = SWAPWINC(packed); - for (uint16_t pairIndex = 0; pairIndex < segment.pairCount; pairIndex++) - { - int16_t fromCoord = SWAPWINC(packed); - int16_t toCoord = SWAPWINC(packed); - Fixed2_14 from(fromCoord); - Fixed2_14 to(toCoord); - ShortFracCorrespondence map{from ,to}; - segment.axisValueMaps.push_back(map); - } - avar_.axisSegmentMaps.push_back(segment); - } -} - -bool TrueTypeFont::IsMakeTupleName(const std::wstring &name) const -{ - std::wstring opt1 = L"User"; - std::wstring opt2 = L"Unnamed"; - - if (name.size() >= opt1.size() && name.compare(0, opt1.size(), opt1) == 0) - return true; - - if (name.size() >= opt2.size() && name.compare(0, opt2.size(), opt2) == 0) - return true; - - return false; -} // TrueTypeFont::IsMakeTupleName - -bool TrueTypeFont::Read(File *file, TrueTypeGlyph *glyph, short *platformID, short *encodingID, wchar_t errMsg[]) { - int32_t glyphIndex; - - this->sfntSize = file->Length(); - - this->AssertMaxSfntSize(this->sfntSize,true,true); - - if (this->sfntSize > this->maxSfntSize) { - MaxSfntSizeError(L"Read: This font is too large",this->sfntSize,errMsg); return false; - } - file->ReadBytes(this->sfntSize, this->sfntHandle); - if (file->Error()) { - swprintf(errMsg,L"Read: I/O error reading this font"); return false; - } - - if (!this->UnpackHeadHheaMaxpHmtx(errMsg)) return false; - - if(*platformID == 3 && *encodingID == 1) - *encodingID = 10; // lets first try 3,10 and default lower if not present - - if (!this->CMapExists(*platformID,*encodingID) && !this->DefaultCMap(platformID,encodingID,errMsg)) return false; - - if (!(this->UnpackGlitsLoca(errMsg) && this->UpdateMaxPointsAndContours(errMsg) && this->UnpackCMap(*platformID,*encodingID,errMsg) && this->UnpackCharGroup(errMsg))) return false; - - // Clear for new font. - if (instanceManager_ != nullptr) - { - instanceManager_->clear(); - } - - if (!this->SetSfnt(*platformID, *encodingID, errMsg)) return false; - - // not the smartest way to get these numbers, another historical legacy - if ((glyphIndex = this->GlyphIndexOf(L'H')) == INVALID_GLYPH_INDEX) this->capHeight = this->unitsPerEm; - else if (this->GetGlyph(glyphIndex,glyph,errMsg)) this->capHeight = glyph->ymax; - else return false; - if ((glyphIndex = this->GlyphIndexOf(L'x')) == INVALID_GLYPH_INDEX) this->xHeight = this->unitsPerEm; - else if (this->GetGlyph(glyphIndex,glyph,errMsg)) this->xHeight = glyph->ymax; - else return false; - if ((glyphIndex = this->GlyphIndexOf(L'p')) == INVALID_GLYPH_INDEX) this->descenderHeight = 0; - else if (this->GetGlyph(glyphIndex,glyph,errMsg)) this->descenderHeight = glyph->ymin; - else return false; - - // Clear for new font. - if (this->postScriptNames) { - delete this->postScriptNames; - this->postScriptNames = NULL; - } - - return true; // by now -} // TrueTypeFont::Read - -bool TrueTypeFont::Write(File *file, wchar_t errMsg[]) { - file->WriteBytes(this->sfntSize, this->sfntHandle); - //if (!file->Error()) file->SetPos(this->sfntSize,true); // truncate file to current size - if (file->Error()) { - swprintf(errMsg,L"I/O error writing this font"); return false; - } - return true; // by now -} // TrueTypeFont::Write - -ControlValueTable *TrueTypeFont::TheCvt(void) { - return this->cvt; -} // TrueTypeFont::TheCvt - -bool TrueTypeFont::GetCvt(TextBuffer *cvtText, wchar_t errMsg[]) { - int32_t size,entries,i; - short *cvt; - wchar_t buffer[maxLineSize]; - - cvt = (short*)this->GetTablePointer(tag_ControlValue); - size = this->GetTableLength(tag_ControlValue); - entries = size >> 1; - if (entries < 0 || MAXCVT < entries) { swprintf(errMsg,L"GetCvt: There are too many cvt entries %li",entries); return false; } - - if (!this->GetSource(true,CVT_GLYPH_INDEX,cvtText,errMsg)) return false; - - if (cvtText->TheLength() == 0) { // decode binary cvt - for (i = 0; i < entries; i++) { swprintf(buffer,L"%i: %hi\r",i,SWAPW(cvt[i])); cvtText->Append(buffer); } - - } - this->cvt->PutCvtBinary(size,(unsigned char *)cvt); - return true; // by now -} // TrueTypeFont::GetCvt - -bool TrueTypeFont::GetPrep(TextBuffer *prepText, wchar_t errMsg[]) { - unsigned char *data; - int32_t size; - - errMsg[0] = L'\0'; - data = this->GetTablePointer(tag_PreProgram); - size = this->GetTableLength(tag_PreProgram); - if (size > MAXBINSIZE) { - swprintf(errMsg,L"GetPrep: pre-program is %li bytes long (cannot be longer than %li bytes)",size,MAXBINSIZE); - return false; - } - return this->UpdateBinData(asmPREP,size,data) && this->GetSource(true,PRE_PGM_GLYPH_INDEX,prepText,errMsg); // get source after binary, in case binary exists but source doesn't... -} // TrueTypeFont::GetPrep - -int32_t TrueTypeFont::PrepBinSize(void) { - return this->binSize[asmPREP]; -} // TrueTypeFont::PrepBinSize - -bool TrueTypeFont::GetFpgm(TextBuffer *fpgmText, wchar_t errMsg[]) { - unsigned char *data; - int32_t size; - - errMsg[0] = L'\0'; - data = this->GetTablePointer(tag_FontProgram); - size = this->GetTableLength(tag_FontProgram); - if (size > MAXBINSIZE) { - swprintf(errMsg,L"GetFpgm: font program is %li bytes long (cannot be longer than %li bytes)",size,MAXBINSIZE); - return false; - } - return this->UpdateBinData(asmFPGM,size,data) && this->GetSource(true,FONT_PGM_GLYPH_INDEX,fpgmText,errMsg); // get source after binary, in case binary exists but source doesn't... -} // TrueTypeFont::GetFpgm - -int32_t TrueTypeFont::FpgmBinSize(void) { - return this->binSize[asmFPGM]; -} // TrueTypeFont::FpgmBinSize - -bool TrueTypeFont::GetGlyf(int32_t glyphIndex, TextBuffer *glyfText, wchar_t errMsg[]) { - return this->GetSource(true,glyphIndex,glyfText,errMsg); - // here we don't get any binary, this is done in GetGlyph, which also deals with the glyph's bounding box or composite information -} // TrueTypeFont::GetGlyf - - -bool TrueTypeFont::GetTalk(int32_t glyphIndex, TextBuffer *talkText, wchar_t errMsg[]) { - return this->GetSource(false,glyphIndex,talkText,errMsg); -} // TrueTypeFont::GetTalk - - -bool TrueTypeFont::GetGlyph(int32_t glyphIndex, TrueTypeGlyph *glyph, wchar_t errMsg[]) { - short i,end = 0; - bool emptyGlyph,weHaveInstructions; - unsigned char *sp; - char signedByte; - short numContoursInGlyph; - unsigned short flags,cgIdx; - short signedWord; - unsigned short unsignedWord; - int32_t numKnots,numContours,componentDepth,binSize; - TrueTypeComponent *component; - int32_t lsb = 0, advWidth = 0; - - if (glyphIndex < 0 || this->numLocaEntries <= glyphIndex) { - swprintf(errMsg,L"TrueTypeFont::GetGlyph: glyphIndex %li is out of range",glyphIndex); return false; - } - - GetHMTXEntry(glyphIndex, &lsb, &advWidth); - - sp = (unsigned char *)this->GetTablePointer(tag_GlyphData) + this->IndexToLoc[glyphIndex]; - emptyGlyph = this->IndexToLoc[glyphIndex] == this->IndexToLoc[glyphIndex+1]; - - glyph->numContoursInGlyph = 0; - glyph->composite = glyph->useMyMetrics = false; - this->UpdateBinData(asmGLYF,0,NULL); - glyph->componentSize = 0; - - if (emptyGlyph) { - F26Dot6 fxXMinMinusLSB = 0 - lsb; - - glyph->xx[LEFTSIDEBEARING] = fxXMinMinusLSB; - glyph->xx[RIGHTSIDEBEARING] = fxXMinMinusLSB + advWidth; - glyph->yy[LEFTSIDEBEARING] = 0; - glyph->yy[RIGHTSIDEBEARING] = 0; - glyph->onCurve[PHANTOMPOINTS] = glyph->onCurve[PHANTOMPOINTS-1] = true; - glyph->onCurve[0] = false; - - glyph->startPoint[0] = glyph->endPoint[0] = 0; - glyph->numContoursInGlyph = 0; - - for (i = 0; i <= end + PHANTOMPOINTS; i++) - { // lsb, rsb - glyph->x[i] = (short)(glyph->xx[i]); // >> places6); - glyph->y[i] = (short)(glyph->yy[i]); // >> places6); - } - } - - if (emptyGlyph) return true; // nothing else we could unpack - - signedWord = READALIGNEDWORD( sp ); - numContoursInGlyph = SWAPW(signedWord); - - if (numContoursInGlyph > MAXCONTOURS) { - swprintf(errMsg,L"GetGlyph: glyph %li has %hi contours (cannot have more than %hi)",glyphIndex,numContoursInGlyph,MAXCONTOURS); return false; - } - - glyph->startPoint[0] = 0; - glyph->endPoint[0] = 0; - glyph->onCurve[0] = true; - glyph->x[0] = 0; - glyph->y[0] = 0; - glyph->xx[0] = 0; - glyph->yy[0] = 0; - - glyph->numContoursInGlyph = 0; - signedWord = READALIGNEDWORD(sp); - glyph->xmin = SWAPW(signedWord); - - signedWord = READALIGNEDWORD(sp); - glyph->ymin = SWAPW(signedWord); - - signedWord = READALIGNEDWORD(sp); - glyph->xmax = SWAPW(signedWord); - - signedWord = READALIGNEDWORD(sp); - glyph->ymax = SWAPW(signedWord); - - glyph->bluePrint.numComponents = 0; - glyph->bluePrint.useMyMetrics = INVALID_GLYPH_INDEX; // use nobody's metrics so far - - if ( numContoursInGlyph < 0 ) { - glyph->composite = true; - weHaveInstructions = false; - int32_t accumulatedKnots = 0; - int32_t accumulatedContours = 0; - do { - if (glyph->bluePrint.numComponents >= MAXNUMCOMPONENTS) { - swprintf(errMsg,L"GetGlyph: glyph %hu has more than %hu components ",glyphIndex,(unsigned short)MAXNUMCOMPONENTS); return false; - } - - component = &glyph->bluePrint.component[glyph->bluePrint.numComponents]; - - flags = *(unsigned short *)(sp+0); flags = SWAPW(flags); - cgIdx = *(unsigned short *)(sp+2); cgIdx = SWAPW(cgIdx); - - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - - weHaveInstructions = (flags & WE_HAVE_INSTRUCTIONS) != 0; // otherwise VC++ 5.0 does the bitwise AND in 16 bit and then moves a BYTE, dropping the WE_HAVE_INSTRUCTIONS (0x0100) bit - - component->glyphIndex = cgIdx; - component->unicode = this->charCodeOf[cgIdx]; - component->rounded = (flags & ROUND_XY_TO_GRID) != 0; - component->anchor = (flags & ARGS_ARE_XY_VALUES) == 0; - component->offsetX = component->offsetY = 0; - component->parent = component->child = 0; - component->transform[0][0] = component->transform[1][1] = 16384; // 1 in 2.14 format - component->transform[0][1] = component->transform[1][0] = 0; - - numKnots = numContours = componentDepth = 0; - if (GetNumPointsAndContours(cgIdx,&numKnots,&numContours,&componentDepth)) { - accumulatedKnots += numKnots; - accumulatedContours += numContours; - if (accumulatedContours > 0 && accumulatedKnots > 0) { - glyph->endPoint[accumulatedContours - 1] = accumulatedKnots - 1; - } - glyph->numContoursInGlyph += numContours; - component->numContours = (unsigned short)numContours; - } else { - swprintf(errMsg,L"GetGlyph: failed to obtain number of contours for component %i of glyph %i",(int32_t)cgIdx,glyphIndex); return false; - } - - if (flags & USE_MY_METRICS) { - glyph->useMyMetrics = true; - if (glyph->bluePrint.useMyMetrics == INVALID_GLYPH_INDEX) glyph->bluePrint.useMyMetrics = cgIdx; // first one wins - } - if ( flags & ARG_1_AND_2_ARE_WORDS ) { - /* arg 1 and 2 */ - signedWord = *(signed short *)(sp+0); component->offsetX = SWAPW(signedWord); - signedWord = *(signed short *)(sp+2); component->offsetY = SWAPW(signedWord); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - } else { - /* arg 1 and 2 as bytes */ - signedByte = *(signed char *)(sp+0); component->offsetX = signedByte; - signedByte = *(signed char *)(sp+1); component->offsetY = signedByte; - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - } - - if (component->anchor) { - component->parent = component->offsetX; component->offsetX = 0; - component->child = component->offsetY; component->offsetY = 0; - } - - if ( flags & WE_HAVE_A_SCALE ) { - /* scale */ - signedWord = *(signed short *)(sp+0); component->transform[0][0] = component->transform[1][1] = SWAPW(signedWord); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { - /* xscale, yscale */ - signedWord = *(signed short *)(sp+0); component->transform[0][0] = SWAPW(signedWord); - signedWord = *(signed short *)(sp+2); component->transform[1][1] = SWAPW(signedWord); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) { - /* xscale, scale01, scale10, yscale */ - signedWord = *(signed short *)(sp+0); component->transform[0][0] = SWAPW(signedWord); - signedWord = *(signed short *)(sp+2); component->transform[0][1] = SWAPW(signedWord); - signedWord = *(signed short *)(sp+4); component->transform[1][0] = SWAPW(signedWord); - signedWord = *(signed short *)(sp+6); component->transform[1][1] = SWAPW(signedWord); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); - } - component->identity = component->transform[0][0] == 16384 && component->transform[1][1] == 16384 // 1 in 2.14 format - && component->transform[0][1] == 0 && component->transform[1][0] == 0; - - if ( glyph->componentSize > MAXCOMPONENTSIZE ) { // maybe too late already... - swprintf(errMsg,L"GetGlyph: glyph %li has %hi bytes of component data (cannot have more than %hi)",glyphIndex,glyph->componentSize,MAXCOMPONENTSIZE); return false; - } - - glyph->bluePrint.numComponents++; - - } while ( flags & MORE_COMPONENTS ); - glyph->ComponentVersionNumber = numContoursInGlyph; - - if (weHaveInstructions) { - binSize = READALIGNEDWORD(sp); - - binSize = SWAPW(binSize); - if (binSize > MAXBINSIZE) { - swprintf(errMsg, L"GetGlyph: glyph %li has %li bytes of instruction (cannot have more than %li)", glyphIndex, binSize, MAXBINSIZE); return false; - } - - this->UpdateBinData(asmGLYF, binSize, sp); - sp += binSize; - } - - } else { - - glyph->numContoursInGlyph = numContoursInGlyph; - - uint8 abyOnCurve[MAXPOINTS]; - numKnots = 0; - - if (numContoursInGlyph > 0) - { - unsignedWord = READALIGNEDWORD(sp); - glyph->endPoint[0] = SWAPW(unsignedWord); - - numKnots = glyph->endPoint[0] + 1; - - for (int32 lContourIndex = 1; lContourIndex < numContoursInGlyph; lContourIndex++) - { - glyph->startPoint[lContourIndex] = glyph->endPoint[lContourIndex - 1] + 1; - - unsignedWord = READALIGNEDWORD(sp); - glyph->endPoint[lContourIndex] = SWAPW(unsignedWord); - - if (numKnots > glyph->endPoint[lContourIndex] || numKnots <= 0) - { - return false; - } - numKnots = glyph->endPoint[lContourIndex] + 1; - } - } - - unsignedWord = READALIGNEDWORD(sp); - binSize = SWAPW(unsignedWord); - - if (binSize > MAXBINSIZE) { - swprintf(errMsg, L"GetGlyph: glyph %li has %li bytes of instruction (cannot have more than %li)", glyphIndex, binSize, MAXBINSIZE); return false; - } - - this->UpdateBinData(asmGLYF, binSize, sp); - sp += binSize; - - // flags - - unsigned short usRepeatCount = 0; - uint8 byRepeatFlag; - - int32 lPointCount = numKnots; - uint8* pbyFlags = abyOnCurve; - - while (lPointCount > 0) - { - if (usRepeatCount == 0) - { - - *pbyFlags = *sp; - - if (*pbyFlags & REPEAT_FLAGS) - { - sp++; - - usRepeatCount = *sp; - } - sp++; - pbyFlags++; - lPointCount--; - } - else - { - byRepeatFlag = pbyFlags[-1]; - lPointCount -= usRepeatCount; - - if (lPointCount < 0) - { - return false; - } - - while (usRepeatCount > 0) - { - *pbyFlags = byRepeatFlag; - pbyFlags++; - usRepeatCount--; - } - } - } - - if (usRepeatCount > 0) - { - return false; - } - - // X - short sXValue = 0; - F26Dot6* pf26OrigX = glyph->xx; - pbyFlags = abyOnCurve; - - for (int32 lPointIndex = 0; lPointIndex < numKnots; lPointIndex++) - { - if (*pbyFlags & XSHORT) - { - if (*pbyFlags & SHORT_X_IS_POS) - { - sXValue += (int16)((uint8)*sp++); - } - else - { - sXValue -= (int16)((uint8)*sp++); - } - } - else if (!(*pbyFlags & NEXT_X_IS_ZERO)) - { - // two bytes - sXValue += SWAPW(*((int16*)sp)); - sp += sizeof(int16); - } - *pf26OrigX = (F26Dot6)sXValue; - pf26OrigX++; - pbyFlags++; - } - - // Y - short sYValue = 0; - F26Dot6* pf26OrigY = glyph->yy; - pbyFlags = abyOnCurve; - - for (int32 lPointIndex = 0; lPointIndex < numKnots; lPointIndex++) - { - if (*pbyFlags & YSHORT) - { - if (*pbyFlags & SHORT_Y_IS_POS) - { - sYValue += (int16)((uint8)*sp++); - } - else - { - sYValue -= (int16)((uint8)*sp++); - } - } - else if (!(*pbyFlags & NEXT_Y_IS_ZERO)) - { - // two bytes - sYValue += SWAPW(*((int16*)sp)); - sp += sizeof(int16); - } - *pf26OrigY = (F26Dot6)sYValue; - pf26OrigY++; - - *pbyFlags &= ONCURVE; - glyph->onCurve[lPointIndex] = *pbyFlags & ONCURVE; - pbyFlags++; - } - } - - end = glyph->endPoint[glyph->numContoursInGlyph - 1]; - - F26Dot6 fxXMinMinusLSB = glyph->xmin - lsb; - glyph->xx[end + 1 + LEFTSIDEBEARING] = fxXMinMinusLSB; - glyph->xx[end + 1 + RIGHTSIDEBEARING] = fxXMinMinusLSB + advWidth; - glyph->yy[end + 1 + LEFTSIDEBEARING] = 0; - glyph->yy[end + 1 + RIGHTSIDEBEARING] = 0; - - for (i = 0; i <= end + PHANTOMPOINTS; i++) - { // lsb, rsb - glyph->x[i] = (short)(glyph->xx[i]); // >> places6); - glyph->y[i] = (short)(glyph->yy[i]); // >> places6); - } - - this->UpdateGlyphProfile(glyph); - this->UpdateMetricProfile(glyph); - - return true; // by now -} // TrueTypeFont::GetGlyph - -int32_t TrueTypeFont::GlyfBinSize(void) { - return this->binSize[asmGLYF]; -} // TrueTypeFont::GlyfBinSize - -unsigned char* TrueTypeFont::GlyfBin(void) { - return this->binData[asmGLYF]; -} - -bool TrueTypeFont::GetHMTXEntry(int32_t glyphIndex, int32_t *leftSideBearing, int32_t *advanceWidth) { - sfnt_HorizontalMetrics *horMetric; - - *leftSideBearing = *advanceWidth = 0; - - if (glyphIndex < 0 || this->numberOfGlyphs <= glyphIndex) glyphIndex = 0; // map to missing glyph - - horMetric = &this->horMetric[glyphIndex]; - *leftSideBearing = horMetric->leftSideBearing; - *advanceWidth = horMetric->advanceWidth; - - return true; // by now -} // TrueTypeFont::GetHMTXEntry - -int32_t TrueTypeFont::NumberOfGlyphs(void) { - return Min(this->GetTableLength(tag_IndexToLoc)/(this->shortIndexToLocTable ? sizeof(short) : sizeof(int32_t)) - 1,this->profile.numGlyphs); - // in a correct font, there are this->profile.numGlyphs + 1 (ie. maxp->numGlyphs + 1) entries in the 'loca' table, however, according to - // claudebe there are still lots of fonts that have an incorrect size for the loca table, so it is safe to use the minimum of the two. -} // TrueTypeFont::NumberOfGlyphs - -bool Compare_UniGlyphMap(UniGlyphMap first, UniGlyphMap second) -{ - // Comparison function that, taking two values of the same type than those - // contained in the list object, returns true if the first argument goes before the - // second argument in the specific order, and false otherwise. - - return (first.unicode < second.unicode); -} - -int32_t TrueTypeFont::GlyphIndexOf(uint32_t charCode) { - std::vector::iterator it; - UniGlyphMap map; - map.unicode = charCode; - - if(!std::binary_search(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap)) - return INVALID_GLYPH_INDEX; - - it = std::lower_bound(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap); - - return it->glyphIndex; -} // TrueTypeFont::GlyphIndexOf - -bool TrueTypeFont::GlyphIndecesOf(wchar_t textString[], int32_t maxNumGlyphIndeces, int32_t glyphIndeces[], int32_t *numGlyphIndeces, wchar_t errMsg[]) { - int32_t i,dec,hex; - wchar_t ch; - - errMsg[0] = (char)0; - i = *numGlyphIndeces = 0; ch = textString[i++]; - while (ch != 0 && *numGlyphIndeces < maxLineSize-1) { - if (ch == L'^') { // (decimal) glyphIndex - ch = textString[i++]; dec = 0; - while (ch && ch != L'^') { - if (L'0' <= ch && ch <= L'9') dec = 10*dec + (int32_t)(ch - L'0'); - else { swprintf(errMsg,L"invalid decimal digit in glyph index"); return false; } - ch = textString[i++]; - } - if (ch == L'^') ch = textString[i++]; - else { swprintf(errMsg,L"closing ^ missing"); return false; } - } else if (ch == L'~') { // (hexadecimal) charCode - ch = textString[i++]; hex = 0; - while (ch && ch != L'~') { - if (L'0' <= ch && ch <= L'9') hex = 16*hex + (int32_t)(ch - L'0'); - else if (L'A' <= ch && ch <= L'F') hex = 16*hex + (int32_t)(ch - L'A' + 10); - else if (L'a' <= ch && ch <= L'f') hex = 16*hex + (int32_t)(ch - L'a' + 10); - else { swprintf(errMsg,L"invalid hexadecimal digit in glyph index"); return false; } - ch = textString[i++]; - } - if (ch == L'~') ch = textString[i++]; - else { swprintf(errMsg,L"closing ~ missing"); return false; } - dec = this->GlyphIndexOf(hex); - } else { - dec = this->GlyphIndexOf(ch); - ch = textString[i++]; - } - if (dec < 0 || this->numberOfGlyphs <= dec) dec = 0; // map to missing glyph - glyphIndeces[(*numGlyphIndeces)++] = dec; - } - - return true; // by now -} // TrueTypeFont::GlyphIndecesOf - -uint32_t TrueTypeFont::CharCodeOf(int32_t glyphIndex) { - return this->charCodeOf[glyphIndex]; -} // TrueTypeFont::CharCodeOf - -uint32_t TrueTypeFont::FirstChar() { - return (glyphIndexMap->begin()->unicode); -} - -uint32_t TrueTypeFont::AdjacentChar(uint32_t charCode, bool forward) { - uint32_t returnCode = 0; - uint32_t size = (uint32_t)glyphIndexMap->size(); - UniGlyphMap map; - map.unicode = charCode; - std::vector::iterator it; - - if(std::binary_search(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap)) { - it = std::lower_bound(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap); - int32_t delta = forward ? 1 : size - 1; - returnCode = glyphIndexMap->at(((it - glyphIndexMap->begin()) + delta) % size).unicode; - }else { - it = std::lower_bound(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap); - int32_t delta = forward ? 0 : size - 1; - returnCode = glyphIndexMap->at(((it - glyphIndexMap->begin()) + delta) % size).unicode; - } - - return returnCode; -} // TrueTypeFont::AdjacentChar - -CharGroup TrueTypeFont::CharGroupOf(int32_t glyphIndex) { - if (glyphIndex < 0 || this->profile.numGlyphs <= glyphIndex) glyphIndex = 0; // safety measure, just in case - return (CharGroup)this->charGroupOf[glyphIndex]; -} // TrueTypeFont::CharGroupOf - -bool TrueTypeFont::CMapExists(short platformID, short encodingID) { - sfnt_char2IndexDirectory *cmap = (sfnt_char2IndexDirectory *)this->GetTablePointer(tag_CharToIndexMap); - short i; - int num = SWAPW(cmap->numTables); - - platformID = SWAPW(platformID); - encodingID = SWAPW(encodingID); - for (i = 0; i < num && (cmap->platform[i].platformID != platformID || cmap->platform[i].specificID != encodingID); i++); - return i < num; -} // TrueTypeFont::CMapExists - -bool TrueTypeFont::DefaultCMap(short *platformID, short *encodingID, wchar_t errMsg[]) { - sfnt_char2IndexDirectory *cmap = (sfnt_char2IndexDirectory *)this->GetTablePointer(tag_CharToIndexMap); - int32_t i,num = SWAPW(cmap->numTables); - - if (num <= 0) { swprintf(errMsg,L"There is no cmap in this font"); return false; } - *platformID = CSWAPW(plat_MS); *encodingID = CSWAPW(10); - for (i = 0; i < num && (cmap->platform[i].platformID != *platformID || cmap->platform[i].specificID != *encodingID); i++); - if (i < num) goto found; // found preferred cmap... - *encodingID = CSWAPW(1); - for (i = 0; i < num && (cmap->platform[i].platformID != *platformID || cmap->platform[i].specificID != *encodingID); i++); - if (i < num) goto found; // found preferred cmap... - for (i = 0; i < num && cmap->platform[i].platformID != *platformID; i++); - if (i < num) { *encodingID = cmap->platform[i].specificID; goto found; } // found cmap in preferred platform... - *platformID = cmap->platform[0].platformID; - *encodingID = cmap->platform[0].specificID; -found: - *platformID = SWAPW(*platformID); *encodingID = SWAPW(*encodingID); - return true; // found any cmap at all -} // TrueTypeFont::DefaultCMap - -bool TrueTypeFont::UnpackCMap(short platformID, short encodingID, wchar_t errMsg[]) { - int32_t i,num; - sfnt_char2IndexDirectory *cmap; - sfnt_mappingTable *map; - - for (i = 0; i < this->maxGlitEntries; i++) this->charCodeOf[i] = 0xFFFF; - this->glyphIndexMap->clear(); - this->numberOfGlyphs = this->NumberOfGlyphs(); - this->numberOfChars = 0; // incremented in EnterChar above... - - cmap = (sfnt_char2IndexDirectory*)this->GetTablePointer(tag_CharToIndexMap); - num = SWAPW(cmap->numTables); - - platformID = SWAPW(platformID); - encodingID = SWAPW(encodingID); - - for (i = 0; i < num && (cmap->platform[i].platformID != platformID || cmap->platform[i].specificID != encodingID); i++); - if (i == num) { swprintf(errMsg,L"Unpacking cmap: cmap for platform id %hi and encoding id %hi not found",platformID,encodingID); return false; } - - map = (sfnt_mappingTable*)(reinterpret_cast(cmap) + SWAPL(cmap->platform[i].offset)); - uint16 format = SWAPW(map->format); - switch (format) { - case 0: this->GetFmt0(map); break; - case 4: this->GetFmt4(map); break; - case 6: this->GetFmt6(map); break; - case 12: this->GetFmt12(map); break; - default: swprintf(errMsg,L"Unpacking cmap: cmap format %hi not implemented",map->format); return false; - } - return true; // by now -} // TrueTypeFont::UnpackCMap - -bool TrueTypeFont::IsCvarTupleData() -{ - uint16_t recordCount = 0; - - if (this->IsVariationFont()) - { - auto instanceManager = this->GetInstanceManager(); - auto cvarTuples = instanceManager->GetCvarTuples(); - - for (auto cvarTuplesIt = cvarTuples->cbegin(); cvarTuplesIt != cvarTuples->cend(); ++cvarTuplesIt) - { - recordCount += (uint16_t)(*cvarTuplesIt)->cvt.size(); - } - } - - return recordCount > 0; -} - -int32_t TrueTypeFont::EstimatePrivateCvar() -{ - int32_t size = 0; - - if (!this->IsVariationFont()) - return size; - - auto instanceManager = this->GetInstanceManager(); - auto tsicInstances = instanceManager->GetPrivateCvarInstances(); - auto axisCount = this->GetVariationAxisCount(); - - bool editedValues = false; - // Any edited values? - for (const auto & instance : *tsicInstances) - { - for (const auto & editedValue : instance->editedCvtValues) - { - if (!editedValues && editedValue.Edited()) - { - editedValues = true; - } - } - } - - // Anything to do? - if (!editedValues) - return size; - - size += sizeof(uint16_t); // majorVersion - size += sizeof(uint16_t); // minorVersion - size += sizeof(uint16_t); // flags - size += sizeof(uint16_t); // axisCount - size += sizeof(uint16_t); // recordCount - size += sizeof(uint16_t); // reserved - - size += sizeof(uint32_t) * axisCount; // AxisArray - size += sizeof(uint16_t) * static_cast(tsicInstances->size()) * axisCount; //RecordLocations - - // TSICRecords - for (const auto & instance : *tsicInstances) - { - size += sizeof(uint16_t); // flags - size += sizeof(uint16_t); // numCVTEntries - size += sizeof(uint16_t); // nameLength - std::wstring name = instance->GetName(); - size += static_cast(name.size() * sizeof(wchar_t)); - - for (const auto & editedValue : instance->editedCvtValues) - { - if (editedValue.Edited()) - { - size += sizeof(uint16_t); // CVTArray entry - size += sizeof(int16_t); // CVTValueArray entry - } - } - } - - return size; -} - -int32_t TrueTypeFont::UpdatePrivateCvar(int32_t *size, unsigned char data[]) -{ - unsigned short *packed = reinterpret_cast(data); - - if (!this->IsVariationFont()) - { - *size = 0; - return *size; - } - - auto instanceManager = this->GetInstanceManager(); - auto tsicInstances = instanceManager->GetPrivateCvarInstances(); - - bool editedValues = false; - // Any edited values? - for (const auto & instance : *tsicInstances) - { - for (const auto & editedValue : instance->editedCvtValues) - { - if (!editedValues && editedValue.Edited()) - { - editedValues = true; - } - } - } - - // Anything to do? - if (!editedValues) - { - *size = 0; - return *size; - } - - TSICHeader header; - header.majorVersion = 1; - // minorVersion 0 tags are mistakenly swapped - // minorVersion 1 tags are not swapped - header.minorVersion = 1; - header.flags = 0; - header.axisCount = this->GetVariationAxisCount(); - header.recordCount = static_cast(tsicInstances->size()); - header.reserved = 0; - auto variationAxisTags = this->GetVariationAxisTags(); - - *packed++ = SWAPW(header.majorVersion); - *packed++ = SWAPW(header.minorVersion); - *packed++ = SWAPW(header.flags); - *packed++ = SWAPW(header.axisCount); - *packed++ = SWAPW(header.recordCount); - *packed++ = SWAPW(header.reserved); - - assert(header.axisCount == variationAxisTags->size()); - // AxisArray - for (const auto & tag : *variationAxisTags) - { - uint32_t* p = reinterpret_cast(packed); - *p = tag; - packed += 2; - } - - // RecordLocations - for (const auto & instance : *tsicInstances) - { - assert(instance->peakF2Dot14.size() == header.axisCount); - for (const auto & axisCoord : instance->peakF2Dot14) - { - uint16_t coord = axisCoord.GetRawValue(); - *packed++ = SWAPW(coord); - } - } - - // TSICRecords - for (const auto & instance : *tsicInstances) - { - TSICRecord record; - record.flags = 0; - - record.numCVTEntries = 0; - for (const auto & editedValue : instance->editedCvtValues) - { - if (editedValue.Edited()) - { - record.numCVTEntries++; - } - } - - *packed++ = SWAPW(record.flags); - *packed++ = SWAPW(record.numCVTEntries); - - record.nameLength = 0; - std::wstring name = instance->GetName(); - if (!instance->IsNamedInstance() && !this->IsMakeTupleName(name)) - { - record.nameLength = static_cast(name.size()); - *packed++ = SWAPW(record.nameLength); - - // NameArray - for (const auto & character : name) - { - *packed++ = SWAPW(character); - } - } - else - { - *packed++ = SWAPW(record.nameLength); - } - - // CVTArray - uint16_t cvt = 0; - for (const auto & editedValue : instance->editedCvtValues) - { - if (editedValue.Edited()) - { - *packed++ = SWAPW(cvt); - } - cvt++; - } - - // CVTValueArray - for (const auto & editedValue : instance->editedCvtValues) - { - if (editedValue.Edited()) - { - int16_t value = editedValue.Value(); - *packed++ = SWAPW(value); - } - } - } - - unsigned char* end = reinterpret_cast(packed); - *size = static_cast(end - data); - - return *size; -} - -bool TrueTypeFont::HasPrivateCvar() -{ - const int32_t privateCvarMinSize = 10 * sizeof(uint16_t) + sizeof(uint32_t); - - int32_t tableLength = this->GetTableLength(PRIVATE_CVAR); - - return(tableLength > privateCvarMinSize && !this->tsicError); -} - -bool TrueTypeFont::GetPrivateCvar(TSICHeader &header) -{ - int32_t tableLength = this->GetTableLength(PRIVATE_CVAR); - - if (tableLength == 0 || !this->IsVariationFont()) - return true; - - unsigned short *packed = reinterpret_cast(this->GetTablePointer(PRIVATE_CVAR)); - if (packed == nullptr) - return true; - - auto tags = this->GetVariationAxisTags(); - if (tags == nullptr) - return true; - - header.majorVersion = SWAPWINC(packed); - header.minorVersion = SWAPWINC(packed); - header.flags = SWAPWINC(packed); - header.axisCount = SWAPWINC(packed); - header.recordCount = SWAPWINC(packed); - header.reserved = SWAPWINC(packed); - - if (header.axisCount != this->GetVariationAxisCount()) - return false; - - // AxisArray - assert(tags->size() == header.axisCount); - for (uint16_t axisIndex = 0; axisIndex < header.axisCount; axisIndex++) - { - uint32_t* ptag = reinterpret_cast(packed); - uint32_t tag = *ptag; - uint32_t swapTag = SWAPL(*ptag); - header.axes.push_back(tag); - if (tag != tags->at(axisIndex) && swapTag != tags->at(axisIndex)) - return false; - - packed += 2; - } - - // RecordLocations - for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) - { - std::vector coords; - for (uint16_t axisIndex = 0; axisIndex < header.axisCount; axisIndex++) - { - uint16_t cval = SWAPWINC(packed); - Fixed2_14 coord(cval); - coords.push_back(coord); - } - header.locations.push_back(coords); - } - - // TSICRecords - for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) - { - TSICRecord record; - record.name = L""; - - record.flags = SWAPWINC(packed); - record.numCVTEntries = SWAPWINC(packed); - record.nameLength = SWAPWINC(packed); - - // NameArray - for (uint16_t nameIndex = 0; nameIndex < record.nameLength; nameIndex++) - { - wchar_t character = SWAPWINC(packed); - record.name += character; - } - - // CVTArray - for (uint16_t cvtIndex = 0; cvtIndex < record.numCVTEntries; cvtIndex++) - { - uint16_t cvt = SWAPWINC(packed); - record.cvts.push_back(cvt); - } - - // CVTValueArray - for (uint16_t cvtIndex = 0; cvtIndex < record.numCVTEntries; cvtIndex++) - { - int16_t cvtValue = SWAPWINC(packed); - record.cvtValues.push_back(cvtValue); - } - - header.tsicRecords.push_back(record); - } - - return true; -} - -bool TrueTypeFont::MergePrivateCvarWithInstanceManager(const TSICHeader &header) -{ - if (!this->IsVariationFont()) - return true; - - auto instanceManager = this->GetInstanceManager(); - // auto axisCount = this->GetVariationAxisCount(); - - std::vector recordMerged; - recordMerged.resize(header.recordCount, false); - - // For each record in private see if there exists same location among instances. - for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) - { - // Get the location - std::vector location = header.locations.at(recordIndex); - // Get the cooresponding Record - const TSICRecord &record = header.tsicRecords[recordIndex]; - assert(record.cvts.size() == record.cvtValues.size()); - - for (uint16_t instanceIndex = 0; instanceIndex < instanceManager->size(); instanceIndex++) - { - Variation::Instance& instance = instanceManager->at(instanceIndex); - std::vector instLoc = instance.GetNormalizedFixed2_14Coordinates(); - if (Variation::IsFixed2_14CoordEqual(location, instLoc)) - { - recordMerged.at(recordIndex) = true; - - // Copy source cvt data to instance - if (record.numCVTEntries > 0) - { - uint16_t highestCvt = 0; - for (const auto & cvt : record.cvts) - highestCvt = std::max(cvt, highestCvt); - - instance.editedCvtValues.clear(); - instance.editedCvtValues.resize(highestCvt + 1, Variation::EditedCvtValue()); - - for (uint16_t recordArrayIndex = 0; recordArrayIndex < record.cvts.size(); recordArrayIndex++) - { - instance.editedCvtValues.at(record.cvts.at(recordArrayIndex)).Update(record.cvtValues.at(recordArrayIndex)); - } - instance.SetAsCvar(true); - } - - // Copy name to instance - if (record.nameLength > 0) - { - instance.SetName(record.name); - } - } - } - } - - // Deal with left over records - for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) - { - if (recordMerged.at(recordIndex) == false) - { - // Get the location - std::vector location = header.locations.at(recordIndex); - // Get the cooresponding Record - const TSICRecord &record = header.tsicRecords[recordIndex]; - assert(record.cvts.size() == record.cvtValues.size()); - - Variation::Instance instance(location); - - // Copy source cvt data to instance - if (record.numCVTEntries > 0) - { - uint16_t highestCvt = 0; - for (const auto & cvt : record.cvts) - highestCvt = std::max(cvt, highestCvt); - - instance.editedCvtValues.resize(highestCvt + 1, Variation::EditedCvtValue()); - - for (uint16_t recordArrayIndex = 0; recordArrayIndex < record.cvts.size(); recordArrayIndex++) - { - instance.editedCvtValues.at(record.cvts.at(recordArrayIndex)).Update(record.cvtValues.at(recordArrayIndex)); - } - instance.SetAsCvar(true); - } - - // Copy name to instance - if (record.nameLength > 0) - { - instance.SetName(record.name); - } - - instanceManager->Add(instance); - } - } - - return true; -} - -int32_t TrueTypeFont::EstimateCvar() -{ - int32_t size = 0; - - if (!this->IsVariationFont()) - return size; - - auto instanceManager = this->GetInstanceManager(); - auto cvarTuples = instanceManager->GetCvarTuples(); - auto axisCount = this->GetVariationAxisCount(); - size_t numTuplesWithData = 0; - size_t numIntermediates = 0; // Among numTuplesWithData how many intermediates - - // Determine how many tuples have data and how many of those have intermediates - for (const auto & tuple : *cvarTuples) - { - if (tuple->cvt.size() > 0) - { - numTuplesWithData += 1; - if (tuple->intermediateStartF2Dot14.size() > 0 || tuple->intermediateEndF2Dot14.size() > 0) - { - numIntermediates += 1; - } - } - } - size = (int32_t)(sizeof(CvarHeader) + // cvar header - numTuplesWithData * (2 * sizeof(USHORT) + axisCount * sizeof(USHORT)) + // TupleVariationHeader for tuples with data - numIntermediates * (2 * axisCount * sizeof(USHORT))); // TupleVariationHeader addition for entries with intermediates (start and end) - - for (const auto & tuple : *cvarTuples) - { - // no data then skip this tuple - if (tuple->cvt.size() == 0) - continue; - - size += (int32_t)(tuple->cvt.size() * 2 * sizeof(USHORT) + 2 * sizeof(USHORT)); - } - - return (numTuplesWithData > 0) ? size : 0; -} - -static bool Compare_Tuples_by_Order(const Variation::CvarTuple & first, const Variation::CvarTuple & second) -{ - // Comparison function that, taking two values of the same type than those - // contained in the list object, returns true if the first argument goes before the - // second argument in the specific order, and false otherwise. - return first.GetWriteOrder() < second.GetWriteOrder(); -} - -int32_t TrueTypeFont::UpdateCvar(int32_t *size, unsigned char data[]) -{ - CvarHeader header; - unsigned short *packed = (unsigned short*)data; - - if (!this->IsVariationFont()) - { - *size = 0; - return *size; - } - - // Copy tuples and then sort according to embedded order. - auto instanceManager = this->GetInstanceManager(); - auto cvarTuples = instanceManager->GetCvarTuplesCopy(); - std::stable_sort(cvarTuples.begin(), cvarTuples.end(), Compare_Tuples_by_Order); - - auto axisCount = this->GetVariationAxisCount(); - // auto tupleCount = cvarTuples.size(); - size_t numTuplesWithData = 0; - size_t numIntermediates = 0; // Among numTuplesWithData how many intermediates - - // Determine how many tuples have data and how many of those have intermediates - for (const auto & tuple : cvarTuples) - { - if (tuple.cvt.size() > 0) - { - numTuplesWithData += 1; - if (tuple.intermediateStartF2Dot14.size() > 0 || tuple.intermediateEndF2Dot14.size() > 0) - { - numIntermediates += 1; - } - } - } - - // if no tuples have data we don't need a cvar table - if (numTuplesWithData == 0) - { - *size = 0; - return *size; - } - - header.majorVersion = 1; - header.minorVersion = 0; - header.tupleVariationCount = (unsigned short)numTuplesWithData; // no flags - header.offsetToData = (unsigned short)(sizeof(CvarHeader) + // cvar header - numTuplesWithData * (2 * sizeof(USHORT) + axisCount * sizeof(USHORT)) + // TupleVariationHeader for tuples with data - numIntermediates * (2 * axisCount * sizeof(USHORT))); // TupleVariationHeader addition for entries with intermediates (start and end) - - *packed++ = SWAPW(header.majorVersion); - *packed++ = SWAPW(header.minorVersion); - *packed++ = SWAPW(header.tupleVariationCount); - *packed++ = SWAPW(header.offsetToData); - - unsigned char* packedByteData = data + header.offsetToData; - unsigned short byteOffset = 0; - - // write serialized data - for (auto cvarTuplesIt = cvarTuples.cbegin(); cvarTuplesIt != cvarTuples.cend(); ++cvarTuplesIt) - { - // no data then skip this tuple - if(cvarTuplesIt->cvt.size() == 0) - continue; - - uint16_t previousByteOffset = byteOffset; - uint16_t recordCount = (uint16_t)cvarTuplesIt->cvt.size(); - uint16_t cvtsToWrite = recordCount; - if (recordCount > SCHAR_MAX) - { - USHORT* packedWordData = (unsigned short*)&packedByteData[byteOffset]; - USHORT c = 0x8000 | recordCount; - *packedWordData = SWAPW(c); - byteOffset += sizeof(USHORT); - } - else - { - unsigned char c = (unsigned char)recordCount; - packedByteData[byteOffset] = c; - byteOffset += sizeof(unsigned char); - } - - // determine highest cvt - uint16_t highestCvt = 0; - for (int cIndex = 0; cIndex < recordCount; ++cIndex) - highestCvt = Max(cvarTuplesIt->cvt[cIndex], highestCvt); - - // write cvt numbers - // break into runs if needed - uint16_t cvtIndex = 0; - uint16_t currentCvt = 0; - while(cvtsToWrite > 0) - { - uint16_t runWrite = Min(cvtsToWrite, POINT_RUN_COUNT_MASK); - - // write control byte for run - unsigned char c = (unsigned char)runWrite - 1; - if (highestCvt > UCHAR_MAX) - c |= POINTS_ARE_WORDS; - packedByteData[byteOffset] = c; - byteOffset += sizeof(unsigned char); - - if (highestCvt <= UCHAR_MAX) - { - // use bytes - for (int wIndex = 0; wIndex < runWrite; ++wIndex) - { - packedByteData[byteOffset] = cvarTuplesIt->cvt[cvtIndex] - currentCvt; - currentCvt = cvarTuplesIt->cvt[cvtIndex++]; - byteOffset += sizeof(unsigned char); - } - } - else - { - // use words - for (int wIndex = 0; wIndex < runWrite; ++wIndex) - { - USHORT* packedWordData = (unsigned short*)&packedByteData[byteOffset]; - uint16_t temp = cvarTuplesIt->cvt[cvtIndex] - currentCvt; - *packedWordData = SWAPW(temp); - currentCvt = cvarTuplesIt->cvt[cvtIndex++]; - byteOffset += sizeof(USHORT); - } - } - cvtsToWrite -= runWrite; - } - - // write deltas - auto deltasToWrite = recordCount; - uint16_t largestDelta = 0; - for (int dIndex = 0; dIndex < recordCount; ++dIndex) - largestDelta = Max(std::abs(cvarTuplesIt->delta[dIndex]), largestDelta); - - cvtIndex = 0; - while (deltasToWrite > 0) - { - // write control byte for run - uint16_t runWrite = Min(deltasToWrite, DELTA_RUN_COUNT_MASK); - unsigned char c = (unsigned char)runWrite - 1; - if (largestDelta > SCHAR_MAX) - c |= DELTAS_ARE_WORDS; - packedByteData[byteOffset] = c; - byteOffset += sizeof(unsigned char); - - if (largestDelta <= SCHAR_MAX) - { - // use bytes - for (int wIndex = 0; wIndex < runWrite; ++wIndex) - { - packedByteData[byteOffset] = (unsigned char)cvarTuplesIt->delta[cvtIndex++]; - byteOffset += sizeof(unsigned char); - } - } - else - { - // use words - for (int wIndex = 0; wIndex < runWrite; ++wIndex) - { - USHORT delta = cvarTuplesIt->delta[cvtIndex++]; - USHORT* packedWordData = (unsigned short*)&packedByteData[byteOffset]; - *packedWordData = SWAPW(delta); - byteOffset += sizeof(USHORT); - } - } - deltasToWrite -= runWrite; - } - - // write header entry for this tuple - uint16_t variationDataSize = byteOffset - previousByteOffset; - *packed++ = SWAPW(variationDataSize); - - // If you have one you need both! - assert(cvarTuplesIt->intermediateEndF2Dot14.size() == cvarTuplesIt->intermediateStartF2Dot14.size()); - - bool intermediate = cvarTuplesIt->intermediateEndF2Dot14.size() > 0 || cvarTuplesIt->intermediateStartF2Dot14.size() > 0; - uint16_t tupleIndex = 0x8000 | 0x2000; - if (intermediate) - tupleIndex = tupleIndex | 0x4000; - - *packed++ = SWAPW(tupleIndex); - - // Write the peakTuple - for (size_t coordIndex = 0; coordIndex < cvarTuplesIt->peakF2Dot14.size(); ++coordIndex) - { - uint16_t coord = cvarTuplesIt->peakF2Dot14[coordIndex].GetRawValue(); - *packed++ = SWAPW(coord); - } - - if (intermediate) - { - // Write the intermediateStartTuple - for (size_t coordIndex = 0; coordIndex < cvarTuplesIt->intermediateStartF2Dot14.size(); ++coordIndex) - { - uint16_t coord = cvarTuplesIt->intermediateStartF2Dot14[coordIndex].GetRawValue(); - *packed++ = SWAPW(coord); - } - - // Write the intermediateEndTuple - for (size_t coordIndex = 0; coordIndex < cvarTuplesIt->intermediateEndF2Dot14.size(); ++coordIndex) - { - uint16_t coord = cvarTuplesIt->intermediateEndF2Dot14[coordIndex].GetRawValue(); - *packed++ = SWAPW(coord); - } - } - } - - *size = header.offsetToData + byteOffset; - - return *size; -} - -void TrueTypeFont::UpdateAdvanceWidthFlag(bool linear) { - sfnt_FontHeader *head; - uint16 flags; - - head = (sfnt_FontHeader*)this->GetTablePointer(tag_FontHeader); - flags = SWAPW(head->flags); - if (linear) flags &= ~USE_INTEGER_SCALING; else flags |= USE_INTEGER_SCALING; - head->flags = SWAPW(flags); -} // TrueTypeFont::UpdateAdvanceWidthFlag - -#define OPTIMIZED_FOR_CLEARTYPE 0x2000 - -bool TrueTypeFont::UpdateBinData(ASMType asmType, int32_t binSize, unsigned char *binData) { - unsigned char *binTemp; - - if (asmType < firstASMType || lastASMType < asmType || binSize < 0) return false; - - binTemp = binSize > 0 ? (unsigned char *)NewP(binSize) : NULL; - if (binSize > 0 && binTemp == NULL) return false; - - if (this->binData[asmType] != NULL) DisposeP((void **)&this->binData[asmType]); - - if (binSize > 0 && binData != NULL) memcpy(binTemp,binData,binSize); - this->binSize[asmType] = binSize; - this->binData[asmType] = binTemp; - - return true; -} // TrueTypeFont::UpdateBinData - -bool TrueTypeFont::TableExists(sfnt_TableTag tag) { - sfnt_OffsetTable *p; - int32_t num,i; - - p = (sfnt_OffsetTable *)this->sfntHandle; - num = SWAPW(p->numOffsets); - tag = SWAPL(tag); - for (i = 0; i < num && p->table[i].tag != tag; i++); - return i < num; // found -} // TrueTypeFont::TableExists - -int32_t TrueTypeFont::GetTableOffset(sfnt_TableTag tag) { - sfnt_OffsetTable *p; - int32_t num,i; - - p = (sfnt_OffsetTable *)this->sfntHandle; - num = SWAPW(p->numOffsets); - tag = SWAPL(tag); - for (i = 0; i < num && p->table[i].tag != tag; i++); - return ((i < num) && (SWAPL(p->table[i].length) > 0)) ? SWAPL(p->table[i].offset) : 0; -} // TrueTypeFont::GetTableOffset - -int32_t TrueTypeFont::GetTableLength(sfnt_TableTag tag) { - sfnt_OffsetTable *p; - int32_t num,i; - - p = (sfnt_OffsetTable *)this->sfntHandle; - num = SWAPW(p->numOffsets); - tag = SWAPL(tag); - for (i = 0; i < num && p->table[i].tag != tag; i++); - return (i < num) ? SWAPL(p->table[i].length) : 0; -} // TrueTypeFont::GetTableLength - -unsigned char *TrueTypeFont::GetTablePointer(sfnt_TableTag tag) { - int32_t offs = this->GetTableOffset(tag); - return offs != 0 ? this->sfntHandle + offs : NULL; -// return *this->sfntHandle + this->GetTableOffset(tag); -} // TrueTypeFont::GetTablePointer - -void UnpackMaxp(unsigned char *sfnt, sfnt_maxProfileTable *profile) { - sfnt_maxProfileTable *maxp = (sfnt_maxProfileTable *)sfnt; - - profile->version = SWAPL(maxp->version); // for this table, set to 1.0 - profile->numGlyphs = SWAPW(maxp->numGlyphs); - profile->maxPoints = SWAPW(maxp->maxPoints); - profile->maxContours = SWAPW(maxp->maxContours); - profile->maxCompositePoints = SWAPW(maxp->maxCompositePoints); - profile->maxCompositeContours = SWAPW(maxp->maxCompositeContours); - profile->maxElements = SWAPW(maxp->maxElements); - profile->maxTwilightPoints = SWAPW(maxp->maxTwilightPoints); - profile->maxStorage = SWAPW(maxp->maxStorage); - profile->maxFunctionDefs = SWAPW(maxp->maxFunctionDefs); - profile->maxInstructionDefs = SWAPW(maxp->maxInstructionDefs); - profile->maxStackElements = SWAPW(maxp->maxStackElements); - profile->maxSizeOfInstructions = SWAPW(maxp->maxSizeOfInstructions); - profile->maxComponentElements = SWAPW(maxp->maxComponentElements); - profile->maxComponentDepth = SWAPW(maxp->maxComponentDepth); -} // UnpackMaxp - -void PackMaxp(unsigned char *sfnt, sfnt_maxProfileTable *profile) { - sfnt_maxProfileTable *maxp = (sfnt_maxProfileTable *)sfnt; - - maxp->version = SWAPL(profile->version); - maxp->numGlyphs = SWAPW(profile->numGlyphs); - maxp->maxPoints = SWAPW(profile->maxPoints); - maxp->maxContours = SWAPW(profile->maxContours); - maxp->maxCompositePoints = SWAPW(profile->maxCompositePoints); - maxp->maxCompositeContours = SWAPW(profile->maxCompositeContours); - maxp->maxElements = SWAPW(profile->maxElements); - maxp->maxTwilightPoints = SWAPW(profile->maxTwilightPoints); - maxp->maxStorage = SWAPW(profile->maxStorage); - maxp->maxFunctionDefs = SWAPW(profile->maxFunctionDefs); - maxp->maxInstructionDefs = SWAPW(profile->maxInstructionDefs); - maxp->maxStackElements = SWAPW(profile->maxStackElements); - maxp->maxSizeOfInstructions = SWAPW(profile->maxSizeOfInstructions); - maxp->maxComponentElements = SWAPW(profile->maxComponentElements); - maxp->maxComponentDepth = SWAPW(profile->maxComponentDepth); -} // PackMaxp - -bool TrueTypeFont::UnpackHeadHheaMaxpHmtx(wchar_t errMsg[]) { - sfnt_FontHeader *phead, head; - sfnt_HorizontalHeader *phhea, hhea; - unsigned short aw,lsb,*hmtx; // actually hhea->numberOf_LongHorMetrics of sfnt_HorizontalMetrics [aw,lsb] pairs the monospaced part where we have only lsb numbers - int32_t glitLen,numEntries,numGlyphs,i,k; - - unsigned char* pmaxp = this->GetTablePointer(tag_MaxProfile); - if (pmaxp == nullptr) - { - swprintf(errMsg, L"Error fetching maxp table"); - return false; - } - UnpackMaxp(pmaxp ,&this->profile); - - phead = (sfnt_FontHeader*)this->GetTablePointer(tag_FontHeader); - if (phead == nullptr) - { - swprintf(errMsg, L"Unpacking head: error fetching table"); - return false; - } - head.version = SWAPL(phead->version); - head.fontRevision = SWAPL(phead->fontRevision); - head.checkSumAdjustment = SWAPL(phead->checkSumAdjustment); - head.magicNumber = SWAPL(phead->magicNumber); - head.flags = SWAPW(phead->flags); - head.unitsPerEm = SWAPW(phead->unitsPerEm); - //phead->created; - //phead->modified; - head.xMin = SWAPW(phead->xMin); - head.yMin = SWAPW(phead->yMin); - head.xMax = SWAPW(phead->xMax); - head.yMax = SWAPW(phead->yMax); - head.macStyle = SWAPW(phead->macStyle); - head.lowestRecPPEM = SWAPW(phead->lowestRecPPEM); - head.fontDirectionHint = SWAPW(phead->fontDirectionHint); - head.indexToLocFormat = SWAPW(phead->indexToLocFormat); - head.glyphDataFormat = SWAPW(phead->glyphDataFormat); - - this->unitsPerEm = head.unitsPerEm; - this->macStyle = head.macStyle; - this->metricProfile.xMin = head.xMin; - this->metricProfile.yMin = head.yMin; - this->metricProfile.xMax = head.xMax; - this->metricProfile.yMax = head.yMax; - if (head.unitsPerEm < 64 || head.unitsPerEm > 16384) { // this used to have an upper range of 17686 - swprintf(errMsg,L"Unpacking head: em-Height %hi not in range 64 through 16384",head.unitsPerEm); return false; - // according to Greg, unitsPerEm > 16384 would cause overflow in the rasterizer, plus imho the number 17686 doesn't make any sense to me. - } - - if (head.indexToLocFormat == SHORT_INDEX_TO_LOC_FORMAT) { - this->shortIndexToLocTable = true; - } else if (head.indexToLocFormat == LONG_INDEX_TO_LOC_FORMAT) { - this->shortIndexToLocTable = false; - } else { - swprintf(errMsg,L"Unpacking head: Unknown indexToLocFormat %hi",head.indexToLocFormat); return false; - } - this->outShortIndexToLocTable = this->shortIndexToLocTable; - - glitLen = this->GetTableLength(PRIVATE_GLIT1); -// it may be, though, that for very old fonts, created with TypeMan, we may run into trouble with the amateur version limitation with this code: -// these fonts may have a (historical) 2048 number for maxGlyphs simply because the glits were allocated for that size, even though the font's -// maxp has less than 256. - numGlyphs = this->NumberOfGlyphs(); - this->maxGlyphs = glitLen == 0 ? this->NumberOfGlyphs() : glitLen/sizeof(sfnt_FileDataEntry) - GLIT_PAD; // for the GLIT_PAD cf comment in UnpackGlitsLoca below - - this->AssertMaxGlyphs(this->maxGlyphs + GLIT_PAD); - - if (this->maxGlyphs + GLIT_PAD > this->maxGlitEntries) { - swprintf(errMsg,L"This font has too many glyphs, please\r" BULLET L" increase the amount of virtual memory in your system settings"); - return false; - } - - phhea = (sfnt_HorizontalHeader*)this->GetTablePointer(tag_HoriHeader); - hhea.version = SWAPL(phhea->version); - hhea.yAscender= SWAPW(phhea->yAscender); - hhea.yDescender= SWAPW(phhea->yDescender); - hhea.yLineGap= SWAPW(phhea->yLineGap); - hhea.advanceWidthMax= SWAPW(phhea->advanceWidthMax); - hhea.minLeftSideBearing= SWAPW(phhea->minLeftSideBearing); - hhea.minRightSideBearing= SWAPW(phhea->minRightSideBearing); - hhea.xMaxExtent= SWAPW(phhea->xMaxExtent); - hhea.horizontalCaretSlopeNumerator= SWAPW(phhea->horizontalCaretSlopeNumerator); - hhea.horizontalCaretSlopeDenominator= SWAPW(phhea->horizontalCaretSlopeDenominator); - hhea.metricDataFormat= SWAPW(phhea->metricDataFormat); - hhea.numberOf_LongHorMetrics= SWAPW(phhea->numberOf_LongHorMetrics); - - this->metricProfile.advanceWidthMax = hhea.advanceWidthMax; - this->metricProfile.minLeftSideBearing = hhea.minLeftSideBearing; - this->metricProfile.minRightSideBearing = hhea.minRightSideBearing; - this->metricProfile.xMaxExtent = hhea.xMaxExtent; - - hmtx = (unsigned short *)this->GetTablePointer(tag_HorizontalMetrics); - k = 0; numEntries = hhea.numberOf_LongHorMetrics; - for (i = 0; i < numEntries; i++) { - aw = hmtx[k++]; - this->horMetric[i].advanceWidth = SWAPW(aw); - lsb = hmtx[k++]; - this->horMetric[i].leftSideBearing = SWAPW(lsb); - } - for (i = numEntries; i < numGlyphs; i++) { // all the remaining ones have the same aw of the last regular [aw,lsb] pair - this->horMetric[i].advanceWidth = SWAPW(aw); - lsb = hmtx[k++]; - this->horMetric[i].leftSideBearing = SWAPW(lsb); - } - - this->InitNewProfiles(); // silence BC errors - - // see comments in TrueTypeFont::UpdateAssemblerProfile - for (i = 0; i < Len(this->maxStackElements); i++) this->maxStackElements[i] = 0; - - return true; // by now -} // TrueTypeFont::UnpackHeadHheaMaxpHmtx - -bool TrueTypeFont::UnpackGlitsLoca(wchar_t errMsg[]) { -/* the 'glit' tables are private tables to index the private tables that store the TMT and TT sources. - however, due to reasons that I don't remember, the (TT) sources for the prep and fpgm, as well as for - the cvt and the hinter parameters, are not stored in individual private tables, but as special glyphs - with glyph indeces 0xFFFA..0xFFFD (as per #defines at the top). on top of that, one of the glyph indeces - holds a magic number, which explains the ominous numberOfGlyph + GLIT_PAD below and the cascade of else ifs upon - unpacking the glit. - Additionnally, the length field on the file is only 16 bits, but the cvt source, which is one of the - glyph, may exceed this range. Therefore, internally the length is kept as 32 bits and computed from two - successive offsets, if the length is the "magic" number 0x8000, rather than taken from the file. In the - end, storing both the offsets (which are always 32 bits) *and* the lengths is redundant, anyhow, but - that is historical. - Finally, if the glits shouldn't contain enough entries, they are extended, by moving the last elements - up in the array. Escapes me, why this is done like this... */ - - int32_t tableLength,progrLength,i,j,numberOfGlyphs,oldMaxGlyph; - sfnt_FileDataEntry *fileGlit; - sfnt_MemDataEntry *glit1, *glit2; - uint32_t *longIndexToLoc; - unsigned short *shortIndexToLoc; - - numberOfGlyphs = this->NumberOfGlyphs(); - - this->glit1Entries = 0; - tableLength = this->GetTableLength(PRIVATE_GLIT1); - progrLength = this->GetTableLength(PRIVATE_PGM1); - if (tableLength > 0 /* && this->glit1Entries == 0 */) { - fileGlit = (sfnt_FileDataEntry*)this->GetTablePointer(PRIVATE_GLIT1); - this->glit1Entries = tableLength / sizeof(sfnt_FileDataEntry); - for (j = 0; j < this->glit1Entries; j++) { - this->glit1[j].glyphCode = SWAPW(fileGlit[j].glyphCode); - - if (j+6 == this->glit1Entries) { // last regular glyph, compute length but skip magic number (cf. below) - this->glit1[j].length = SWAPL(fileGlit[j+2].offset) - SWAPL(fileGlit[j].offset); - } else if (j+5 == this->glit1Entries) { // magic number, has no length - this->glit1[j].length = 0; - } else if (j+1 == this->glit1Entries) { // last special "glyph", compute length from table length - this->glit1[j].length = progrLength - SWAPL(fileGlit[j].offset); - } else if ((unsigned short)SWAPW(fileGlit[j].length) != (unsigned short)0x8000) { // length is short - // unless cast to unsigned short, compiler will sign extend the result of SWAPW to 32 bit signed, - // turning 0x8000 into 0ffff8000 and subsequently compare it to 0x00008000 !!! - this->glit1[j].length = SWAPW(fileGlit[j].length); - } else { // length is (regular case) long - this->glit1[j].length = SWAPL(fileGlit[j+1].offset) - SWAPL(fileGlit[j].offset); - } - this->glit1[j].offset = SWAPL(fileGlit[j].offset); - } - } - - this->glit2Entries = 0; - tableLength = this->GetTableLength(PRIVATE_GLIT2); - progrLength = this->GetTableLength(PRIVATE_PGM2); - if (tableLength > 0 /* && this->glit2Entries == 0 */) { - fileGlit = (sfnt_FileDataEntry*)this->GetTablePointer(PRIVATE_GLIT2); - this->glit2Entries = tableLength / sizeof(sfnt_FileDataEntry); - for (j = 0; j < this->glit2Entries; j++) { - this->glit2[j].glyphCode = SWAPW(fileGlit[j].glyphCode); - - if (j+6 == this->glit2Entries) { - this->glit2[j].length = SWAPL(fileGlit[j+2].offset) - SWAPL(fileGlit[j].offset); - } else if (j+5 == this->glit2Entries) { - this->glit2[j].length = 0; - } else if (j+1 == this->glit2Entries) { - this->glit2[j].length = progrLength - SWAPL(fileGlit[j].offset); - } else if ((unsigned short)SWAPW(fileGlit[j].length) != (unsigned short)0x8000) { - // unless cast to unsigned short, compiler will sign extend the result of SWAPW to 32 bit signed, - // turning 0x8000 into 0ffff8000 and subsequently compare it to 0x00008000 !!! - this->glit2[j].length = SWAPW(fileGlit[j].length); - } else { - this->glit2[j].length = SWAPL(fileGlit[j+1].offset) - SWAPL(fileGlit[j].offset); - } - this->glit2[j].offset = SWAPL(fileGlit[j].offset); - } - } - -// claudebe I know that there should be a better place to put this but I assume that -// we need to read again the loca table at the same time as we need to read the glit table -// this is an ugly programming under heavy time stress !!! - - longIndexToLoc = (uint32_t *)this->GetTablePointer(tag_IndexToLoc ); - shortIndexToLoc = (unsigned short *)longIndexToLoc; - this->numLocaEntries = this->GetTableLength(tag_IndexToLoc)/(this->shortIndexToLocTable ? sizeof(short) : sizeof(int32_t)) - 1; - for (i = 0; i <= this->numLocaEntries; i++) - this->IndexToLoc[i] = this->shortIndexToLocTable ? ((int32_t)((unsigned short)SWAPW(shortIndexToLoc[i]))) << 1 : SWAPL(longIndexToLoc[i]); - -// for (i = 0; i < Min(numberOfGlyphs,this->numLocaEntries); i++) // 2nd Ed Win98 fonts somehow got a loca table with one extra entry - for (i = 0; i < this->numLocaEntries; i++) // 2nd Ed Win98 fonts somehow got a loca table with one extra entry - if (this->IndexToLoc[i] > this->IndexToLoc[i+1]) { - swprintf(errMsg,L"Unpacking loca: loca table not in ascending order %i %i %i",i, this->IndexToLoc[i], this->IndexToLoc[i + 1]); - return false; - } - - if (tableLength > 0 && (numberOfGlyphs + GLIT_PAD > this->glit1Entries || numberOfGlyphs + GLIT_PAD > this->glit2Entries)) { - oldMaxGlyph = this->glit1Entries - GLIT_PAD; - this->maxGlyphs = numberOfGlyphs; - glit1 = this->glit1; - glit2 = this->glit2; - for (i = this->maxGlyphs + 4,j = oldMaxGlyph + 4; i >= this->maxGlyphs ; i--, j-- ) { - glit1[i].glyphCode = glit1[j].glyphCode; - glit1[i].length = glit1[j].length; - glit1[i].offset = glit1[j].offset; - - glit2[i].glyphCode = glit2[j].glyphCode; - glit2[i].length = glit2[j].length; - glit2[i].offset = glit2[j].offset; - } - for (i = oldMaxGlyph; i < this->maxGlyphs ; i++ ) { - glit1[i].glyphCode = (short)i; - glit1[i].length = 0; - glit1[i].offset = 0; - - glit2[i].glyphCode = (short)i; - glit2[i].length = 0; - glit2[i].offset = 0; - } - this->glit1Entries = this->maxGlyphs + GLIT_PAD; - this->glit2Entries = this->maxGlyphs + GLIT_PAD; - } - return true; // by now -} // TrueTypeFont::UnpackGlitsLoca - -bool TrueTypeFont::UpdateMaxPointsAndContours(wchar_t errMsg[]) { - // fonts like the Trebuchet have insufficient values for maxPoints, maxContours, maxCompositePoints, and maxCompositeContours, - // which creates a bootstrapping problem, given that TrueTypeFont::GetGlyph expects a correct font to be installed in the rasterizer. - // If this fails, we never get a chance to update the above profile values, hence we do it in here for prophylactic reasons... - - const wchar_t failureMsg[3][32] = {L"invalid 'maxp'", L"invalid 'glyf'", L"corrupt 'glyf'"}; - - int32_t glyphIndex,numGlyphs,numKnots,numContours,componentDepth,maxPoints,maxContours,maxCompositePoints,maxCompositeContours,failureCode; - unsigned char *maxpTable,*glyfTable; - - failureCode = 0; - maxpTable = this->GetTablePointer(tag_MaxProfile); - if (maxpTable == NULL) goto failure; - - failureCode = 1; - glyfTable = this->GetTablePointer(tag_GlyphData); - if (glyfTable == NULL) goto failure; - - failureCode = 2; - numGlyphs = this->numLocaEntries; - maxPoints = 0; maxCompositePoints = 0; - maxContours = 0; maxCompositeContours = 0; - for (glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { - numKnots = numContours = componentDepth = 0; - - if (!this->GetNumPointsAndContours(glyphIndex,&numKnots,&numContours,&componentDepth)) goto failure; - - if (componentDepth == 0) { // simple glyph - maxPoints = Max(maxPoints,numKnots); - maxContours = Max(maxContours,numContours); - } else { // composite glyph - maxCompositePoints = Max(maxCompositePoints,numKnots); - maxCompositeContours = Max(maxCompositeContours,numContours); - } - } - - UnpackMaxp(maxpTable,&this->profile); - this->profile.maxPoints = (uint16)maxPoints; - this->profile.maxContours = (uint16)maxContours; - this->profile.maxCompositePoints = (uint16)maxCompositePoints; - this->profile.maxCompositeContours = (uint16)maxCompositeContours; - PackMaxp(maxpTable,&this->profile); - - return true; -failure: - swprintf(errMsg,L"Failed to update max points and contours due to " WIDE_STR_FORMAT L" table",failureMsg[failureCode]); - return false; -} // TrueTypeFont::UpdateMaxPointsAndContours - -void TrueTypeFont::EnterChar(int32_t glyphIndex, uint32_t charCode) { - UniGlyphMap entry; - entry.glyphIndex = (unsigned short) glyphIndex; - entry.unicode = charCode; - glyphIndexMap->insert(glyphIndexMap->end(),entry); - - this->charCodeOf[glyphIndex] = charCode; - this->numberOfChars++; -} // TrueTypeFont::EnterChar - -void TrueTypeFont::SortGlyphMap() { - std::sort(glyphIndexMap->begin(), glyphIndexMap->end(), Compare_UniGlyphMap); -} - -void TrueTypeFont::GetFmt0(sfnt_mappingTable *map) { // I made no attempt to understand this - int32_t i; - uint8 *glyphIdArray = (uint8 *)(&map[1]); - - for (i = 0; i < 256; i++) this->EnterChar(glyphIdArray[i],i); - - this->SortGlyphMap(); -} // TrueTypeFont::GetFmt0 - -typedef struct { - uint16 segCountX2; - uint16 searchRange; - uint16 entrySelector; - uint16 rangeShift; - uint16 endCount[1]; -} sfnt_cmap4hdr; - -void TrueTypeFont::GetFmt4(sfnt_mappingTable *map) { // I made no attempt to understand this - unsigned short j; - int16 i,segCount,*idDelta,delta; - uint16 gid, end, *endCount, start, *startCount, *idRangeOffset, offset, *glyphIdArray; - sfnt_cmap4hdr *cmap4hdr = (sfnt_cmap4hdr*)(&map[1]); - - segCount = SWAPW(cmap4hdr->segCountX2) >> 1; - endCount = (uint16 *)(&(cmap4hdr->endCount[0])); - startCount = endCount + (segCount + 1); - idDelta = (int16 *)&startCount[segCount]; - idRangeOffset = (uint16 *)&idDelta[segCount]; - glyphIdArray = &idRangeOffset[segCount]; - - - for (i = 0; i < segCount && endCount[i] != 0xFFFF; i++) { - start = SWAPW(startCount[i]); - end = SWAPW(endCount[i]); - delta = SWAPW(idDelta[i]); - offset = SWAPW(idRangeOffset[i]); - - if (0 == offset) { - for (j = start; j <= end; j++) this->EnterChar((uint16)(j + delta), j); - } else { - for (j = start; j <= end; j++) { - gid = idRangeOffset[offset/2 + j - start + i]; - this->EnterChar((uint16)SWAPW(gid), j); - } - } - } - this->SortGlyphMap(); -} // TrueTypeFont::GetFmt4 - -void TrueTypeFont::GetFmt6(sfnt_mappingTable *map) { // I made no attempt to understand this - int32_t i,entries,firstCode,glyphIndex; - sfnt_mappingTable6 *Fmt6Table = (sfnt_mappingTable6 *)(reinterpret_cast(map) + sizeof(sfnt_mappingTable6)); - - firstCode = (int32_t)SWAPW(Fmt6Table->firstCode); - entries = (int32_t)SWAPW(Fmt6Table->entryCount); - for (i = 0; i < entries; i++) { - glyphIndex = SWAPW(Fmt6Table->glyphIdArray[i]); - this->EnterChar(glyphIndex,firstCode + i); - } - this->SortGlyphMap(); -} // TrueTypeFont::GetFmt6 - -void TrueTypeFont::GetFmt12(sfnt_mappingTable *map) -{ - sfnt_mappingTable12 *fntTable = reinterpret_cast(map); - sfnt_mappingTable12Record *record; - uint32 i, j, count, nGroups = SWAPL(fntTable->nGroups); - - for (i = 0; i < nGroups; i++) { - record = reinterpret_cast(reinterpret_cast(map) + sizeof(sfnt_mappingTable12) + i * sizeof(sfnt_mappingTable12Record)); - uint32 startCharCode = SWAPL(record->startCharCode); - uint32 endCharCode = SWAPL(record->endCharCode); - uint32 startGlyphID = SWAPL(record->startGlyphCode); - for ( j = startCharCode, count = 0; j <= endCharCode; j++, count++) - this->EnterChar(startGlyphID + count, j); - } - this->SortGlyphMap(); -} - -bool TrueTypeFont::UnpackCharGroup(wchar_t errMsg[]) { - int32_t i,numberOfGlyph; - short *charGroup,tempGroup; - - for (i = 0; i < this->maxGlitEntries; i++) this->charGroupOf[i] = otherCase; - - if (this->TableExists(PRIVATE_GROUP)) { - numberOfGlyph = this->GetTableLength(PRIVATE_GROUP) >> 1; - if (numberOfGlyph <= this->maxGlitEntries) { - charGroup = (short *)this->GetTablePointer(PRIVATE_GROUP); - - for (i = 0; i < numberOfGlyph; i++) { - tempGroup = SWAPW(charGroup[i]); - if (tempGroup < TOTALTYPE) tempGroup = intInFileToCharGroup[tempGroup]; // translate old char groups - this->charGroupOf[i] = (unsigned char)tempGroup; - } - - } - } - return true; // by now -} // TrueTypeFont::UnpackCharGroup - -bool TrueTypeFont::GetSource(bool lowLevel, int32_t glyphIndex, TextBuffer *source, wchar_t errMsg[]) { - sfnt_MemDataEntry *glit; - int32_t len,glitEntries,glitIndex,pgmID; - - if (lowLevel) { - pgmID = PRIVATE_PGM1; glit = this->glit1; glitEntries = this->glit1Entries; - } else { - pgmID = PRIVATE_PGM2; glit = this->glit2; glitEntries = this->glit2Entries; - } - - for (glitIndex = 0; glitIndex < glitEntries && glit[glitIndex].glyphCode != glyphIndex; glitIndex++); - len = this->GetTableLength(pgmID); - if (len > 0 && glitIndex < glitEntries && glit[this->maxGlyphs].offset == PRIVATE_STAMP_1) - { - source->SetText(glit[glitIndex].length,(char*)(this->GetTablePointer(pgmID) + glit[glitIndex].offset)); - } - else - { - swprintf(errMsg,L"Unpacking source: "); - if (len == 0) - swprintf(&errMsg[STRLENW(errMsg)],L"private " WIDE_STR_FORMAT L"level table empty",lowLevel ? L"low" : L"high"); - else if (glitIndex == glitEntries) - swprintf(&errMsg[STRLENW(errMsg)],L"glyph %li not in private glit",glyphIndex); - else - swprintf(&errMsg[STRLENW(errMsg)],L"bad private stamp 1"); - source->SetText(0,(wchar_t*)NULL); - return len == 0; // ok to have no sources - } - return true; // by now -} // TrueTypeFont::GetSource - -bool TrueTypeFont::GetTTOTable(sfnt_TableTag srcTag, TextBuffer *src, sfnt_TableTag binTag, ASMType asmType) { - src->SetText(this->GetTableLength(srcTag),(char *)this->GetTablePointer(srcTag)); - return this->UpdateBinData(asmType,this->GetTableLength(binTag),this->GetTablePointer(binTag)); -} // TrueTypeFont::GetTTOTable - - -/* fix this */ // huh? I don't claim to understand all of this in BuildNewSfnt & sqq... -#define variableTables 16 - -class TableAvailability -{ -public: - TableAvailability() : tag_(0), required_(false), strip_(stripNothing) {} - TableAvailability(sfnt_TableTag tag, bool required, StripCommand strip) : tag_(tag), required_(required), strip_(strip) {} - - sfnt_TableTag Tag() const - { - return tag_; - } - - bool Required() const - { - return required_; - } - - StripCommand Strip() const - { - return strip_; - } - -private: - sfnt_TableTag tag_; - bool required_; // to work with VTT - StripCommand strip_; // i.e. strip if incoming strip >= this value for strip -}; - -void SwapEntries(sfnt_DirectoryEntry tbl[], int32_t *a, int32_t *b) { - sfnt_DirectoryEntry entry; - int32_t i; - - entry = tbl[*a]; tbl[*a] = tbl[*b]; tbl[*b] = entry; - i = *a; *a = *b; *b = i; -} // SwapEntries - -void FillNewGlit(sfnt_MemDataEntry glit[], int32_t maxGlyphs, int32_t glitEntries) { - int32_t i,j; - - for (i = 0; i < glitEntries; i++) { - glit[i].glyphCode = (unsigned short)i; - glit[i].length = 0; - glit[i].offset = 0; - } - glit[maxGlyphs].offset = PRIVATE_STAMP_1; // Stamp it - for (i = maxGlyphs, j = 0; j < GLIT_PAD; i++, j++) glit[i].glyphCode = glitPadIndex[j]; -} // FillNewGlit - -uint32_t PackGlit(sfnt_FileDataEntry *fileGlit, int32_t glitEntries, sfnt_MemDataEntry *memGlit) { - int32_t i,length; - - for (i = 0; i < glitEntries; i++) { - fileGlit[i].glyphCode = SWAPW(memGlit[i].glyphCode); - - length = memGlit[i].length > 0x08000 ? 0x08000 : (unsigned short)memGlit[i].length; - fileGlit[i].length = SWAPW(length); - fileGlit[i].offset = SWAPL(memGlit[i].offset); - } - - return glitEntries*sizeof(sfnt_FileDataEntry); -} // PackGlit - -bool BuildOffsetTables(unsigned char *sfnt, uint32_t maxSfntSize, sfnt_OffsetTable *offsetTable, sfnt_OffsetTable *tmpOffsetTable, int32_t numVariableTables, TableAvailability avail[], StripCommand strip, int32_t *headerSize) { - int32_t i,j,j2,maxPowerOf2,headIdx,glyfIdx,locaIdx,hheaIdx,hmtxIdx,tag; - sfnt_DirectoryEntry entry = {(sfnt_TableTag)0x3F3F3F3F /* '????' */, 0, 0, 0}; // tag, checksum, offset, length - -// first, do some sanity check on the offset table, to be done upon reading a font? -// another inherited hack: a flat MYOFFSETTABLESIZE bytes are allocated for the internal copy of the sfnt directory -// so first assert that this is enough to hold all directory entries (see sfnt.h for type definitions) -// to do so, very first thing is to get the numOffsets - memcpy((char*)offsetTable,(char*)sfnt,OFFSETTABLESIZE); - offsetTable->numOffsets = SWAPW(offsetTable->numOffsets); - assert(sizeof(sfnt_OffsetTable) + (offsetTable->numOffsets - 1)*sizeof(sfnt_DirectoryEntry) <= MYOFFSETTABLESIZE); -// now get the rest - memcpy((char*)&offsetTable->table[0],(char*)&sfnt[OFFSETTABLESIZE],MYOFFSETTABLESIZE-OFFSETTABLESIZE); - - for (i = 0; i < offsetTable->numOffsets; i++) { - if (offsetTable->table[i].length != 0) { - offsetTable->table[i].offset = SWAPL(offsetTable->table[i].offset); - offsetTable->table[i].length = SWAPL(offsetTable->table[i].length); - if (offsetTable->table[i].offset > maxSfntSize) return false; - } else { - offsetTable->table[i].offset = 0; - } - offsetTable->table[i].tag = SWAPL(offsetTable->table[i].tag); - offsetTable->table[i].checkSum = SWAPL(offsetTable->table[i].checkSum); - } - -// now build a temporary offset table containing the tables to be used in the sfnt to be built - memcpy((char*)tmpOffsetTable,(char*)offsetTable, MYOFFSETTABLESIZE); - for (i = 0, j2 = tmpOffsetTable->numOffsets; i < numVariableTables; i++) { - // all tables that we need - for (j = 0; j < tmpOffsetTable->numOffsets && tmpOffsetTable->table[j].tag != avail[i].Tag(); j++) - ; - if (avail[i].Required() && j == tmpOffsetTable->numOffsets) { // required but not present - entry.tag = avail[i].Tag(); - tmpOffsetTable->table[j2++] = entry; // add - } - } - tmpOffsetTable->numOffsets = (short)j2; - for (j = 0, j2 = 0; j < tmpOffsetTable->numOffsets; j++) { - // all tables that we have already - for (i = 0; i < numVariableTables && tmpOffsetTable->table[j].tag != avail[i].Tag(); i++) - ; - if (i == numVariableTables || strip < avail[i].Strip()) { - // not strippable at all or not high enough stripping priviledges - tmpOffsetTable->table[j2++] = tmpOffsetTable->table[j]; // keep - } - } - tmpOffsetTable->numOffsets = (short)j2; - -// finish temporary offset table - tmpOffsetTable->version = 0x00010000; - maxPowerOf2 = 2; - for (tmpOffsetTable->entrySelector = 0; maxPowerOf2 <= tmpOffsetTable->numOffsets; tmpOffsetTable->entrySelector++) maxPowerOf2 <<= 1; - maxPowerOf2 >>= 1; - tmpOffsetTable->searchRange = (uint16)(maxPowerOf2 * 16); - tmpOffsetTable->rangeShift = tmpOffsetTable->numOffsets * 16 - tmpOffsetTable->searchRange; - -// remainder of sfnt starts at offset 'headerSize' - *headerSize = OFFSETTABLESIZE + tmpOffsetTable->numOffsets*sizeof(sfnt_DirectoryEntry); // Header - -// make sure we get to do 'head' and 'glyf' before 'loca' because 'loca' depends on them; -// likewise, 'hmtx' depends on 'hhea' (numberOfHMetrics), which depends on 'glyf' (metrics, useMyMetrics) - headIdx = glyfIdx = locaIdx = hheaIdx = hmtxIdx = 0; - for (i = 0; i < tmpOffsetTable->numOffsets; i++) { - tag = tmpOffsetTable->table[i].tag; - if (tag == tag_FontHeader) headIdx = i; - else if (tag == tag_GlyphData) glyfIdx = i; - else if (tag == tag_IndexToLoc) locaIdx = i; - else if (tag == tag_HoriHeader) hheaIdx = i; - else if (tag == tag_HorizontalMetrics) hmtxIdx = i; - } - if (glyfIdx > hheaIdx) SwapEntries(tmpOffsetTable->table,&glyfIdx,&hheaIdx); - if (hheaIdx > hmtxIdx) SwapEntries(tmpOffsetTable->table,&hheaIdx,&hmtxIdx); - if (headIdx > locaIdx) SwapEntries(tmpOffsetTable->table,&headIdx,&locaIdx); - if (glyfIdx > locaIdx) SwapEntries(tmpOffsetTable->table,&glyfIdx,&locaIdx); - - return true; // by now -} // BuildOffsetTables - -bool TrueTypeFont::BuildNewSfnt(StripCommand strip, CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, - TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - wchar_t errMsg[]) { - unsigned char *sfnt,*tmpSfnt,*tmpSfntHandle; - - int32_t headerSize,i,j,tag,numberOfGlyphs,numberOfHMetrics = 0,zero = 0L,pad; - uint32_t sfntPos = 0,tmpSfntSize,sizeOfTable,newSfntSizeEstimate; - sfnt_FontHeader *head; - sfnt_HorizontalHeader *hhea; - bool result = false; - - // fileGlit will point into tmpSfnt as it is being built (tags PRIVATE_GLIT1 and PRIVATE_GLIT2) - // its correct setup is relied on upon packing PRIVATE_PGM1 and PRIVATE_PGM2 (not BeatS design...) - sfnt_MemDataEntry *memGlit = NULL; - sfnt_FileDataEntry *fileGlit = NULL; - - // cannot have variables in initialization of array, hence... - TableAvailability avail[variableTables]; - - avail[ 0] = TableAvailability(tag_ControlValue, this->cvt->HighestCvtNum() >= 0, stripHints); // 0 - avail[ 1] = TableAvailability(tag_FontProgram, this->binSize[asmFPGM] > 0, stripHints); // 1 - avail[ 2] = TableAvailability(tag_HoriDeviceMetrics, this->hdmxBinSize > 0, stripBinary); // 2 - avail[ 3] = TableAvailability(tag_LinearThreshold, this->ltshBinSize > 0, stripBinary); // 3 - avail[ 4] = TableAvailability(tag_VertDeviceMetrics, this->vdmxBinSize > 0, stripBinary); // 4 - avail[ 5] = TableAvailability(tag_GridfitAndScanProc, this->gaspBinSize > 0, stripEverything); // 5 - avail[ 6] = TableAvailability(tag_PreProgram, this->binSize[asmPREP] > 0, stripHints); // 6 - avail[ 7] = TableAvailability(tag_NamingTable, true, stripEverything); // 7 - avail[ 8] = TableAvailability(PRIVATE_GLIT1, true, stripSource); // 8 - avail[ 9] = TableAvailability(PRIVATE_PGM1, true, stripSource); // 9 - avail[10] = TableAvailability(PRIVATE_GLIT2, true, stripSource); // 10 - avail[11] = TableAvailability(PRIVATE_PGM2, true, stripSource); // 11 - avail[12] = TableAvailability(PRIVATE_GROUP, true, stripSource); // 12 - avail[13] = TableAvailability(PRIVATE_CMAP, false, stripNothing); // 13 - avail[14] = TableAvailability(tag_CVTVariations, this->IsCvarTupleData(), stripHints); // 14 - avail[15] = TableAvailability(PRIVATE_CVAR, this->IsCvarTupleData(), stripSource); // 15 - - if (strip >= stripSource) { - cvtText-> Delete(0,cvtText-> TheLength()); - fpgmText->Delete(0,fpgmText->TheLength()); - prepText->Delete(0,prepText->TheLength()); - glyfText->Delete(0,glyfText->TheLength()); - talkText->Delete(0,talkText->TheLength()); - } - - head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); - if (SWAPL(head->magicNumber) != SFNT_MAGIC) { - swprintf(errMsg,L"BuildNewSfnt: Bad magic number in the head"); - goto term; - } - - if (glyphIndex < 0 || this->profile.numGlyphs <= glyphIndex) { - swprintf(errMsg,L"BuildNewSfnt: invalid glyph index %li of current glyph",glyphIndex); - goto term; - } - - if (strip >= stripHints) { - //for (i = 0; i < numASMTypes; i++) this->UpdateBinData((ASMType)i,0,NULL); - for (i = firstTTASMType; i <= lastTTASMType; i++) this->UpdateBinData((ASMType)i,0,NULL); - this->cvt->PutCvtBinary(0,NULL); // in hopes that copying 0 bytes from address NULL doesn't cause a bus-error... - } - -// numberOfGlyphs = this->NumberOfGlyphs(); -// we have to use profile.numGlyphs here because the font may have glyphs added to it while the loca table has not yet been updated and NumberOfGlyphs() uses the loca table... - numberOfGlyphs = this->profile.numGlyphs; - - this->UpdateGlyphProfile(glyph); - this->PackMaxpHeadHhea(); - - sfnt = this->sfntHandle; // alias it - - if (!BuildOffsetTables(sfnt,this->sfntSize,this->offsetTable,this->tmpOffsetTable,variableTables,avail,strip,&headerSize)) { - swprintf(errMsg,L"BuildNewSfnt: offset is too large"); // claudebe, problem with a new font received from Sampo - goto term; - } - -#ifdef _DEBUG -// given MYOFFSETTABLESIZE for the size of the entire sfnt_OffsetTable, maxNumDirectoryEntries denotes -// the (truncated) number of sfnt_DirectoryEntry entries that this amount of memory is good for -// (see comments about inherited hack above) -#define maxNumDirectoryEntries ((MYOFFSETTABLESIZE - sizeof(sfnt_OffsetTable))/sizeof(sfnt_DirectoryEntry) + 1) - uint32_t sizeEstimate[maxNumDirectoryEntries]; - char tagCopy[maxNumDirectoryEntries][8]; - for (i = 0; i < maxNumDirectoryEntries; i++) { sizeEstimate[i] = 0; tagCopy[i][0] = 0; } -#endif - - // 1. pass: get a reasonnable upper limit for the expected file size - newSfntSizeEstimate = headerSize; - - for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { - - switch (tag = this->tmpOffsetTable->table[i].tag) { - - case tag_FontHeader: - sizeOfTable = this->tmpOffsetTable->table[i].length; - break; - case tag_HoriHeader: { - sizeOfTable = this->tmpOffsetTable->table[i].length; - break; - } - case tag_HorizontalMetrics: - // discovered in an old version of Courier New Italic, glyph "E" had a different AW than a composite using "E" (and E's metrics) - // which was correctly propagated to the hmtx upon PackGlyph, after which point the actual packing routine for the hmtx worked - // with a different numberOfHMetrics triggering an assert because a differing table size was detected - // Therefore, for size estimate always assume each glyph as a different AW - sizeOfTable = numberOfGlyphs*sizeof(sfnt_HorizontalMetrics); - break; - case tag_GlyphData: - sizeOfTable = this->GetPackedGlyphsSizeEstimate(glyph,glyphIndex,this->IndexToLoc); - break; - case tag_IndexToLoc: - // for size estimate, always assume long format 'loca' table; correct this->outShortIndexToLocTable determined in PackGlyph only - sizeOfTable = (numberOfGlyphs + 1)*sizeof(int32_t); - break; - case PRIVATE_GROUP: - sizeOfTable = numberOfGlyphs*sizeof(short); - break; - case PRIVATE_GLIT1: - case PRIVATE_GLIT2: { - int32_t glitEntries; - bool newGlit; - - newGlit = this->tmpOffsetTable->table[i].length == 0 || this->tmpOffsetTable->table[i].offset == 0; // we've probably just added the table - - if (!newGlit) - glitEntries = tag == PRIVATE_GLIT1 ? this->glit1Entries : this->glit2Entries; - else { - glitEntries = this->maxGlyphs + GLIT_PAD; - memGlit = tag == PRIVATE_GLIT1 ? this->glit1 : this->glit2; - FillNewGlit(memGlit,this->maxGlyphs,glitEntries); - } - - sizeOfTable = glitEntries*sizeof(sfnt_FileDataEntry); - break; - } - case PRIVATE_CVAR: - sizeOfTable = this->tsicBinSize = this->EstimatePrivateCvar(); - break; - case PRIVATE_PGM1: - case PRIVATE_PGM2: - sizeOfTable = GetPackedGlyphSourcesSize(glyfText,prepText,cvtText,talkText,fpgmText, - tag == PRIVATE_PGM1 ? 1 : 2,glyphIndex,tag == PRIVATE_PGM1 ? this->glit1 : this->glit2); - break; - case tag_PreProgram: - sizeOfTable = this->binSize[asmPREP]; - break; - case tag_FontProgram: - sizeOfTable = this->binSize[asmFPGM]; - break; - case tag_ControlValue: - sizeOfTable = this->cvt->GetCvtBinarySize(); - break; - case tag_GridfitAndScanProc: - sizeOfTable = this->gaspBinSize ? this->gaspBinSize : this->tmpOffsetTable->table[i].length; - break; - case tag_CVTVariations: - sizeOfTable = this->cvarBinSize = this->EstimateCvar(); - break; - case tag_NamingTable: - sizeOfTable = this->tmpOffsetTable->table[i].length == 0 ? 6 : this->tmpOffsetTable->table[i].length; - break; - default: - sizeOfTable = this->tmpOffsetTable->table[i].length; - break; - } // switch (tag) - -#ifdef _DEBUG - sizeEstimate[i] = sizeOfTable; - - tagCopy[i][0] = (char)(tag >> 24); - tagCopy[i][1] = (char)(tag >> 16); - tagCopy[i][2] = (char)(tag >> 8); - tagCopy[i][3] = (char)(tag); - tagCopy[i][4] = (char)0; -#endif - - sizeOfTable = DWordPad(sizeOfTable); - newSfntSizeEstimate += sizeOfTable; - } - - this->AssertMaxSfntSize(newSfntSizeEstimate,false,true); // make sure we have a large enough tmpSfnt - - if (newSfntSizeEstimate > this->maxTmpSfntSize) { - MaxSfntSizeError(L"BuildNewSfnt: This font is getting too large",newSfntSizeEstimate,errMsg); goto term; - } - - // 2. pass: build the new sfnt into the tmpSfntHandle - - tmpSfnt = this->sfntTmpHandle; // alias this one, too - - sfntPos = headerSize; // skip header, need to fill directory first, then plug in header as final step - - for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { - - switch (tag = this->tmpOffsetTable->table[i].tag) { - - case tag_FontHeader: { -#ifdef _DEBUG - sizeOfTable = this->tmpOffsetTable->table[i].length; -#endif - memcpy((char*) &tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); - head = (sfnt_FontHeader *)&tmpSfnt[sfntPos]; - long long temp_time = DateTime(); // ADD GREGH 20101209 - int32_t temp_lower = (int32_t)(temp_time & 0xFFFFFFFF); // ADD GREGH 20101209 - int32_t temp_upper = (int32_t)((temp_time >> 32) & 0xFFFFFFFF); // ADD GREGH 20101209 - head->modified.bc = SWAPL(temp_upper); // ADD GREGH 20101209 - head->modified.ad = SWAPL(temp_lower); // ADD GREGH 20101209 -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - } - case tag_HoriHeader: { // determine numberOfHMetrics, required in tag_HorizontalMetrics below!!! - int32_t aw; -#ifdef _DEBUG - sizeOfTable = this->tmpOffsetTable->table[i].length; -#endif - memcpy((char*) &tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); - - aw = this->horMetric[numberOfGlyphs-1].advanceWidth; - for (numberOfHMetrics = numberOfGlyphs; numberOfHMetrics >= 2 && this->horMetric[numberOfHMetrics-2].advanceWidth == aw; numberOfHMetrics--); - - hhea = (sfnt_HorizontalHeader *)&tmpSfnt[sfntPos]; - hhea->numberOf_LongHorMetrics = CSWAPW((unsigned short)numberOfHMetrics); -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - - } - case tag_HorizontalMetrics: { - int32_t s,d; - unsigned short *hmtx; -#ifdef _DEBUG - // for size estimate always assume each glyph as a different AW - sizeOfTable = numberOfGlyphs*sizeof(sfnt_HorizontalMetrics); -#endif - hmtx = (unsigned short *)&tmpSfnt[sfntPos]; - s = d = 0; - while (s < numberOfHMetrics) { - hmtx[d++] = SWAPW(this->horMetric[s].advanceWidth); - hmtx[d++] = SWAPW(this->horMetric[s].leftSideBearing); - s++; - } - while (s < numberOfGlyphs) { - // remainder of hmtx consists of lsb's only - hmtx[d++] = SWAPW(this->horMetric[s].leftSideBearing); - s++; - } - - this->tmpOffsetTable->table[i].length = d*sizeof(short); -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length <= sizeOfTable); -#endif - break; - } - - case tag_GlyphData: -#ifdef _DEBUG - sizeOfTable = this->GetPackedGlyphsSizeEstimate(glyph,glyphIndex,this->IndexToLoc); -#endif - memcpy((char*)this->tmpIndexToLoc,(char*)this->IndexToLoc, (numberOfGlyphs + 1) * sizeof(int32_t)); - - this->tmpOffsetTable->table[i].length = this->PackGlyphs(strip,glyph,glyphIndex,this->IndexToLoc,this->tmpIndexToLoc,&tmpSfnt[sfntPos]); -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length <= sizeOfTable); -#endif - break; - - case tag_IndexToLoc: - // here 'head' is (still) valid and pointing to the header of the sfnt being built - - // for size estimate, always assume long format 'loca' table; correct this->outShortIndexToLocTable determined in PackGlyph only -#ifdef _DEBUG - sizeOfTable = (numberOfGlyphs + 1)*sizeof(int32_t); -#endif - if (this->outShortIndexToLocTable) { - unsigned short *shortIndexToLoc = (unsigned short *)&tmpSfnt[sfntPos]; - - this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(short); - head->indexToLocFormat = CSWAPW(SHORT_INDEX_TO_LOC_FORMAT); - for (j = 0; j <= numberOfGlyphs; j++) - { - unsigned short tempIdx = (unsigned short)(this->tmpIndexToLoc[j] >> 1); - shortIndexToLoc[j] = SWAPW(tempIdx); - } - } else { - int32_t *longIndexLoca = (int32_t *)&tmpSfnt[sfntPos]; - - this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(int32_t); - head->indexToLocFormat = CSWAPW(LONG_INDEX_TO_LOC_FORMAT); - for (j = 0; j <= numberOfGlyphs; j++) - { - longIndexLoca[j] = SWAPL(this->tmpIndexToLoc[j]); - } - } -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length <= sizeOfTable); -#endif - break; - - case PRIVATE_GROUP: { - int32_t j; - short *charGroup,tempGroup; - -#ifdef _DEBUG - sizeOfTable = numberOfGlyphs*sizeof(short); -#endif - this->tmpOffsetTable->table[i].length = numberOfGlyphs * sizeof(short); - charGroup = (short *)&tmpSfnt[sfntPos]; - this->charGroupOf[glyphIndex] = group; - for (j = 0; j < numberOfGlyphs; j++) { - tempGroup = this->charGroupOf[j]; - if (tempGroup < TOTALTYPE) tempGroup = charGroupToIntInFile[tempGroup]; // translate old char groups - charGroup[j] = SWAPW(tempGroup); - } -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - } - - case PRIVATE_GLIT1: - case PRIVATE_GLIT2: { - int32_t glitEntries; - bool newGlit; - - newGlit = this->tmpOffsetTable->table[i].length == 0 || this->tmpOffsetTable->table[i].offset == 0; // we've probably just added the table - - if (tag == PRIVATE_GLIT1) { - memGlit = this->glit1; - glitEntries = this->glit1Entries; - } else { // tag == PRIVATE_GLIT2 - memGlit = this->glit2; - glitEntries = this->glit2Entries; - } - if (newGlit) { - glitEntries = this->maxGlyphs + GLIT_PAD; - FillNewGlit(memGlit,this->maxGlyphs,glitEntries); - } -#ifdef _DEBUG - sizeOfTable = glitEntries*sizeof(sfnt_FileDataEntry); -#endif - fileGlit = (sfnt_FileDataEntry *)&tmpSfnt[sfntPos]; // need to assign for case PRIVATE_PGM right below (not BeatS architecture...) - this->tmpOffsetTable->table[i].length = PackGlit(fileGlit,glitEntries,memGlit); - -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - } - - case PRIVATE_PGM1: - case PRIVATE_PGM2: { - // this assumes that we do the PRIVATE_GLIT1 and 2 prior to the respective PRIVATE_PGM1 and 2, - // which is a valid assumption as long as VTT is the only instance to add and remove either of them, - // otherwise the fileGlit and memGlit below are not pointing to the correct locations in the sfnt being built -#ifdef _DEBUG - sizeOfTable = GetPackedGlyphSourcesSize(glyfText,prepText,cvtText,talkText,fpgmText, - tag == PRIVATE_PGM1 ? 1 : 2,glyphIndex,memGlit); -#endif - this->PackGlyphSources(glyfText,prepText,cvtText,talkText,fpgmText,tag == PRIVATE_PGM1 ? 1 : 2,glyphIndex, - fileGlit,memGlit,(uint32_t*)(&this->tmpOffsetTable->table[i].length),&tmpSfnt[sfntPos]); -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - - } - - case PRIVATE_CVAR: - sizeOfTable = this->tsicBinSize; - this->UpdatePrivateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); - break; - - case tag_PreProgram: -#ifdef _DEBUG - sizeOfTable = this->binSize[asmPREP]; -#endif - memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmPREP],this->binSize[asmPREP]); - this->tmpOffsetTable->table[i].length = this->binSize[asmPREP]; -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - case tag_FontProgram: -#ifdef _DEBUG - sizeOfTable = this->binSize[asmFPGM]; -#endif - memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmFPGM],this->binSize[asmFPGM]); - this->tmpOffsetTable->table[i].length = this->binSize[asmFPGM]; -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - - case tag_ControlValue: -#ifdef _DEBUG - sizeOfTable = this->cvt->GetCvtBinarySize(); -#endif - this->cvt->GetCvtBinary((int32_t*)&this->tmpOffsetTable->table[i].length,&tmpSfnt[sfntPos]); -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - - // case tag_HoriDeviceMetrics: not handled in VTT anymore... - // case tag_LinearThreshold: not handled in VTT anymore... - // case tag_VertDeviceMetrics: not handled in VTT anymore... - - case tag_CVTVariations: - sizeOfTable = this->cvarBinSize; - this->UpdateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); - break; - - case tag_GridfitAndScanProc: - if (this->gaspBinSize) { - - unsigned short *packed; - int32_t s; -#ifdef _DEBUG - sizeOfTable = this->gaspBinSize; -#endif - this->tmpOffsetTable->table[i].length = this->gaspBinSize; - packed = (unsigned short *)&tmpSfnt[sfntPos]; - *packed++ = SWAPW(this->gaspTable.version); - *packed++ = SWAPW(this->gaspTable.numRanges); - for (s = 0; s < this->gaspTable.numRanges; s++) { - unsigned short rangeMaxPPEM = this->gaspTable.gaspRange.at(s).rangeMaxPPEM; - unsigned short rangeGaspBehavior = this->gaspTable.gaspRange.at(s).rangeGaspBehavior; - *packed++ = SWAPW(rangeMaxPPEM); - *packed++ = SWAPW(rangeGaspBehavior); - } -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - } else { - // if this->gaspBinSize = 0 then VTT hasn't changed gasp table, so copy from original - goto copyOld; - } - break; - - case tag_NamingTable: - if (this->tmpOffsetTable->table[i].length == 0) { // we've just added a name table; does this make sense at all? we're not editing name tables in here (anymore) -#ifdef _DEBUG - sizeOfTable = 6; -#endif - this->tmpOffsetTable->table[i].length = 6; - memcpy((char*)&tmpSfnt[sfntPos], "\0\0\0\0\0\6", 6L); // dummy name table -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - } else { - goto copyOld; - } - - break; - - default: - copyOld: -#ifdef _DEBUG - sizeOfTable = this->tmpOffsetTable->table[i].length; -#endif - memcpy((char*)&tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); - -#ifdef _DEBUG - assert(this->tmpOffsetTable->table[i].length == sizeOfTable); -#endif - break; - } // switch (tag) -#ifdef _DEBUG - assert(sizeEstimate[i] == sizeOfTable && sizeEstimate[i] >= this->tmpOffsetTable->table[i].length); -#endif - this->tmpOffsetTable->table[i].offset = sfntPos; - sfntPos += this->tmpOffsetTable->table[i].length; - - pad = DWordPad(sfntPos) - sfntPos; - memcpy((char*)&tmpSfnt[sfntPos],(char*)&zero,pad); // zero pad for long word alignment - sfntPos += pad; - } -#ifdef _DEBUG - assert(newSfntSizeEstimate >= sfntPos); -#endif -// once we've finished plugging in all the tables, plug in the directory (temporary offset table) - - { sfnt_DirectoryEntry *entry; - - entry = &(this->tmpOffsetTable->table[0]); - for (i=0; itmpOffsetTable->numOffsets; i++, entry++) - { - entry->tag = SWAPL(entry->tag); - entry->checkSum = SWAPL(entry->checkSum); - entry->offset = SWAPL(entry->offset); - entry->length = SWAPL(entry->length); - } - this->tmpOffsetTable->version = SWAPL(this->tmpOffsetTable->version); - this->tmpOffsetTable->numOffsets = SWAPW(this->tmpOffsetTable->numOffsets); - this->tmpOffsetTable->searchRange = SWAPW(this->tmpOffsetTable->searchRange); - this->tmpOffsetTable->entrySelector = SWAPW(this->tmpOffsetTable->entrySelector); - this->tmpOffsetTable->rangeShift = SWAPW(this->tmpOffsetTable->rangeShift); - } - - memcpy((char*)tmpSfnt,(char*)this->tmpOffsetTable, headerSize); - - result = true; // by now - -term: // function termination code - if (result) { // successful so far, now swap sfntTmpHandle <-> sfntHandle and install new sfnt; swap => no resize/handle deallocation/handle copy/memory fragmentation - tmpSfntHandle = this->sfntHandle; this->sfntHandle = this->sfntTmpHandle; this->sfntTmpHandle = tmpSfntHandle; - tmpSfntSize = this->maxSfntSize; this->maxSfntSize = this->maxTmpSfntSize; this->maxTmpSfntSize = tmpSfntSize; - this->sfntSize = sfntPos; - - this->CalculateNewCheckSums(); - this->CalculateCheckSumAdjustment(); - this->SortTableDirectory(); - - // claudebe the glit entries must be updated - this->glit1Entries = this->glit2Entries = 0; - this->shortIndexToLocTable = this->outShortIndexToLocTable; - - result = this->UnpackGlitsLoca(errMsg) && this->UpdateMaxPointsAndContours(errMsg) && this->UnpackCharGroup(errMsg) && this->SetSfnt( -1, -1, errMsg); - } - return result; -} // TrueTypeFont::BuildNewSfnt - -#define maxNumGlyphs 0x10000 // 64k -#define minDynamicArraySize 0x100000 // 1 MB - -typedef struct { - int32_t size,used; - int32_t dataPos[maxNumGlyphs]; - unsigned char *data; -} GlyphTableArray; - -bool InitDynamicArray(GlyphTableArray *dyn) { - dyn->data = (unsigned char *)NewP(minDynamicArraySize); - dyn->size = dyn->data != NULL ? minDynamicArraySize : 0; - dyn->used = 0; - return dyn->data != NULL; -} // InitDynamicArray - -bool AssertDynamicArraySize(GlyphTableArray *dyn, int32_t deltaSize) { - int32_t newSize; - unsigned char *tmpData; - - if (dyn->used + deltaSize <= dyn->size) return true; - - newSize = ((AddReserve(dyn->size + deltaSize) + minDynamicArraySize - 1)/minDynamicArraySize)*minDynamicArraySize; - tmpData = (unsigned char *)NewP(newSize); - if (tmpData != NULL) { - memcpy(tmpData,dyn->data,dyn->used); - DisposeP((void **)&dyn->data); - dyn->size = newSize; - dyn->data = tmpData; - } - return tmpData != NULL; -} // AssertDynamicArraySize - -bool TermDynamicArray(GlyphTableArray *dyn) { - if (dyn->data != NULL) DisposeP((void **)&dyn->data); - dyn->size = 0; - return true; -} // TermDynamicArray - -typedef struct { - bool binaryOnly; - - int32_t numGlyphs,currGlyph; - - GlyphTableArray binary; - sfnt_HorizontalMetrics hmtx[maxNumGlyphs]; - - // for binaryOnly == false - GlyphTableArray asmSrc; - GlyphTableArray vttSrc; - - unsigned char glyfGroup[maxNumGlyphs]; - bool composite[maxNumGlyphs]; -} IncrBuildSfntData; - -bool TrueTypeFont::GetNumPointsAndContours(int32_t glyphIndex, int32_t *numKnots, int32_t *numContours, int32_t *componentDepth) { - unsigned char *data; - int16 contours,knots; - uint16 flags,glyph; - int32_t glyphSize; - - data = this->GetTablePointer(tag_GlyphData); - if (data == NULL) - return false; - - if (glyphIndex < 0 || this->numLocaEntries <= glyphIndex) - return false; - data += this->IndexToLoc[glyphIndex]; - - glyphSize = this->IndexToLoc[glyphIndex+1] - this->IndexToLoc[glyphIndex]; - if (glyphSize == 0) return true; // we've probably just added a range of empty glyphs in the process of importing glyphs - if (glyphSize < 0) - return false; // this would be bad, however.,. - - contours = READALIGNEDWORD(data); contours = SWAPW(contours); - data += 4*sizeof(uint16); // bounding box; - if (contours < 0) { // composite glyph - (*componentDepth)++; - if (*componentDepth >= MAXCOMPONENTSIZE) - return false; - do { - flags = READALIGNEDWORD(data); flags = SWAPW(flags); - glyph = READALIGNEDWORD(data); glyph = SWAPW(glyph); - if (!(GetNumPointsAndContours(glyph,numKnots,numContours,componentDepth))) - return false; - if (flags & ARG_1_AND_2_ARE_WORDS) - data += 2*sizeof(uint16); - else - data += sizeof(uint16); - if (flags & WE_HAVE_A_SCALE) - data += sizeof(uint16); - else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) - data += 2*sizeof(uint16); - else if (flags & WE_HAVE_A_TWO_BY_TWO) - data += 4*sizeof(uint16); - } while (flags & MORE_COMPONENTS); - } else { // (leaf) glyph - if (*numContours + (int32_t)contours >= MAXCONTOURS) - return false; - *numContours += (int32_t)contours; - if (contours > 0) { - data += (contours-1)*sizeof(uint16); - knots = READALIGNEDWORD(data); knots = SWAPW(knots) + 1; // look at end point array and add 1 - if (*numKnots + (int32_t)knots >= MAXPOINTS) - return false; - *numKnots += (int32_t)knots; - } - } - return true; // by now -} // TrueTypeFont::GetNumPointsAndContours - -bool TrueTypeFont::IncrBuildNewSfnt( wchar_t errMsg[]) { -#define variableTablesForIncrBuild 10 - IncrBuildSfntData *iSfnt; - - errMsg[0] = L'\0'; - - if (this->incrBuildSfntData == NULL) { swprintf(errMsg, L"this->incrBuildSfntData is NULL"); return false; } - iSfnt = (IncrBuildSfntData *)this->incrBuildSfntData; - - StripCommand strip = iSfnt->binaryOnly ? stripSource : stripNothing; - - unsigned char *sfnt, *tmpSfnt, *tmpSfntHandle; - - int32_t headerSize, i, j, j2, tag, numberOfGlyphs = 0, numberOfHMetrics = 0, zero = 0L, pad; - uint32_t sfntPos = 0, tmpSfntSize, sizeOfTable, newSfntSizeEstimate; - sfnt_FontHeader *head; - bool result = false; - - TableAvailability avail[variableTablesForIncrBuild]; - - avail[0] = TableAvailability(tag_ControlValue, true, stripHints); - avail[1] = TableAvailability(tag_FontProgram, true, stripHints); - avail[2] = TableAvailability(tag_PreProgram, true, stripHints); - avail[3] = TableAvailability(PRIVATE_GLIT1, true, stripSource); - avail[4] = TableAvailability(PRIVATE_PGM1, true, stripSource); - avail[5] = TableAvailability(PRIVATE_GLIT2, true, stripSource); - avail[6] = TableAvailability(PRIVATE_PGM2, true, stripSource); - avail[7] = TableAvailability(PRIVATE_GROUP, true, stripSource); - avail[8] = TableAvailability(tag_CVTVariations, this->IsCvarTupleData(), stripHints); - avail[9] = TableAvailability(PRIVATE_CVAR, this->IsCvarTupleData(), stripSource); - - head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); - if (SWAPL(head->magicNumber) != SFNT_MAGIC) { - swprintf(errMsg,L"IncrBuildNewSfnt: Bad magic number in the head"); - goto term; - } - - numberOfGlyphs = this->NumberOfGlyphs(); - - this->PackMaxpHeadHhea(); - - sfnt = this->sfntHandle; // alias it - - if (!BuildOffsetTables(sfnt,this->sfntSize,this->offsetTable,this->tmpOffsetTable,variableTablesForIncrBuild,avail,strip,&headerSize)) { - swprintf(errMsg,L"IncrBuildNewSfnt: offset is too large"); // claudebe, problem with a new font received from Sampo - goto term; - } - -#ifdef _DEBUG -// given MYOFFSETTABLESIZE for the size of the entire sfnt_OffsetTable, maxNumDirectoryEntries denotes -// the (truncated) number of sfnt_DirectoryEntry entries that this amount of memory is good for -// (see comments about inherited hack above) -#define maxNumDirectoryEntries ((MYOFFSETTABLESIZE - sizeof(sfnt_OffsetTable))/sizeof(sfnt_DirectoryEntry) + 1) - uint32_t sizeEstimate[maxNumDirectoryEntries]; - char tagCopy[maxNumDirectoryEntries][8]; - for (i = 0; i < maxNumDirectoryEntries; i++) { sizeEstimate[i] = 0; tagCopy[i][0] = 0; } -#endif - - // 1. pass: get a reasonnable upper limit for the expected file size - newSfntSizeEstimate = headerSize; - - for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { - - switch (tag = this->tmpOffsetTable->table[i].tag) { - - case tag_HorizontalMetrics: - // discovered in an old version of Courier New Italic, glyph "E" had a different AW than a composite using "E" (and E's metrics) - // which was correctly propagated to the hmtx upon PackGlyph, after which point the actual packing routine for the hmtx worked - // with a different numberOfHMetrics triggering an assert because a differing table size was detected - // Therefore, for size estimate always assume each glyph as a different AW - sizeOfTable = numberOfGlyphs*sizeof(sfnt_HorizontalMetrics); - break; - case tag_GlyphData: - sizeOfTable = iSfnt->binary.used; - break; - case tag_IndexToLoc: - // for size estimate, always assume long format 'loca' table; correct this->outShortIndexToLocTable determined in PackGlyph only - sizeOfTable = (numberOfGlyphs + 1)*sizeof(int32_t); - break; - case PRIVATE_GROUP: - sizeOfTable = numberOfGlyphs*sizeof(short); - break; - case PRIVATE_GLIT1: - case PRIVATE_GLIT2: - sizeOfTable = (numberOfGlyphs + GLIT_PAD)*sizeof(sfnt_FileDataEntry); - break; - case PRIVATE_CVAR: - sizeOfTable = this->tsicBinSize = this->EstimatePrivateCvar(); - break; - case PRIVATE_PGM1: - sizeOfTable = iSfnt->asmSrc.used; - break; - case PRIVATE_PGM2: - sizeOfTable = iSfnt->vttSrc.used; - break; - case tag_PreProgram: - sizeOfTable = this->binSize[asmPREP]; - break; - case tag_FontProgram: - sizeOfTable = this->binSize[asmFPGM]; - break; - case tag_ControlValue: - sizeOfTable = this->cvt->GetCvtBinarySize(); - break; - case tag_GridfitAndScanProc: - sizeOfTable = this->gaspBinSize ? this->gaspBinSize : this->tmpOffsetTable->table[i].length; - break; - case tag_CVTVariations: - this->cvarBinSize = this->EstimateCvar(); - sizeOfTable = this->cvarBinSize ? this->cvarBinSize : this->tmpOffsetTable->table[i].length; - break; - default: - sizeOfTable = this->tmpOffsetTable->table[i].length; - break; - } // switch (tag) - -#ifdef _DEBUG - tagCopy[i][0] = (char)(tag >> 24); - tagCopy[i][1] = (char)(tag >> 16); - tagCopy[i][2] = (char)(tag >> 8); - tagCopy[i][3] = (char)(tag); - tagCopy[i][4] = (char)0; -#endif - - sizeOfTable = DWordPad(sizeOfTable); - newSfntSizeEstimate += sizeOfTable; - } - - this->AssertMaxSfntSize(newSfntSizeEstimate,false,true); // make sure we have a large enough tmpSfnt - - if (newSfntSizeEstimate > this->maxTmpSfntSize) { - MaxSfntSizeError(L"IncrBuildNewSfnt: This font is getting too large",newSfntSizeEstimate,errMsg); goto term; - } - - // 2. pass: build the new sfnt into the tmpSfntHandle - tmpSfnt = this->sfntTmpHandle; // alias this one, too - - sfntPos = headerSize; // skip header, need to fill directory first, then plug in header as final step - - for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { - - switch (tag = this->tmpOffsetTable->table[i].tag) { - - case tag_FontHeader: { - memcpy((char*) &tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); - head = (sfnt_FontHeader *)&tmpSfnt[sfntPos]; - long long temp_time = DateTime(); // ADD GREGH 20101209 - uint32_t temp_lower = (uint32_t)(temp_time & 0xFFFFFFFF); // ADD GREGH 20101209 - int32_t temp_upper = (int32_t)((temp_time >> 32) & 0xFFFFFFFF); // ADD GREGH 20101209 - head->modified.bc = SWAPL(temp_upper); // ADD GREGH 20101209 - head->modified.ad = SWAPL(temp_lower); // ADD GREGH 20101209 - - break; - } - case tag_HoriHeader: { // determine numberOfHMetrics, required in tag_HorizontalMetrics below!!! - int32_t aw; - sfnt_HorizontalHeader *hhea; - - memcpy((char *)&tmpSfnt[sfntPos],(char *)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); - - aw = iSfnt->hmtx[numberOfGlyphs-1].advanceWidth; - for (numberOfHMetrics = numberOfGlyphs; numberOfHMetrics >= 2 && iSfnt->hmtx[numberOfHMetrics-2].advanceWidth == aw; numberOfHMetrics--); - - hhea = (sfnt_HorizontalHeader *)&tmpSfnt[sfntPos]; - hhea->numberOf_LongHorMetrics = CSWAPW((unsigned short)numberOfHMetrics); - break; - } - case tag_HorizontalMetrics: { - int32_t s,d; - unsigned short *hmtx; - - hmtx = (unsigned short *)&tmpSfnt[sfntPos]; - s = d = 0; - while (s < numberOfHMetrics) { - hmtx[d++] = SWAPW(iSfnt->hmtx[s].advanceWidth); - hmtx[d++] = SWAPW(iSfnt->hmtx[s].leftSideBearing); - s++; - } - while (s < numberOfGlyphs) { - // remainder of hmtx consists of lsb's only - hmtx[d++] = SWAPW(iSfnt->hmtx[s].leftSideBearing); - s++; - } - - this->tmpOffsetTable->table[i].length = d*sizeof(short); - break; - } - - case tag_GlyphData: { - this->tmpOffsetTable->table[i].length = iSfnt->binary.used; - memcpy(&tmpSfnt[sfntPos],iSfnt->binary.data,iSfnt->binary.used); - break; - } - case tag_IndexToLoc: - // here 'head' is (still) valid and pointing to the header of the sfnt being built - - this->outShortIndexToLocTable = iSfnt->binary.used < 65535*2; - if (this->outShortIndexToLocTable) { - unsigned short tempIdx; - unsigned short *shortIndexToLoc = (unsigned short *)&tmpSfnt[sfntPos]; - - this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(short); - head->indexToLocFormat = CSWAPW(SHORT_INDEX_TO_LOC_FORMAT); - for (j = 0; j < numberOfGlyphs; j++){ - tempIdx = (unsigned short)(iSfnt->binary.dataPos[j] >> 1); - shortIndexToLoc[j] = SWAPW(tempIdx); - } - tempIdx = (unsigned short)(iSfnt->binary.used >> 1); - shortIndexToLoc[numberOfGlyphs] = SWAPW(tempIdx); - } else { - int32_t *longIndexLoca = (int32_t *)&tmpSfnt[sfntPos]; - - this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(int32_t); - head->indexToLocFormat = CSWAPW(LONG_INDEX_TO_LOC_FORMAT); - for (j = 0; j < numberOfGlyphs; j++) { - longIndexLoca[j] = SWAPL(iSfnt->binary.dataPos[j]); - } - longIndexLoca[numberOfGlyphs] = SWAPL(iSfnt->binary.used); - } - break; - - case PRIVATE_GROUP: { - int32_t j; - short *charGroup,tempGroup; - - this->tmpOffsetTable->table[i].length = numberOfGlyphs * sizeof(short); - charGroup = (short *)&tmpSfnt[sfntPos]; - for (j = 0; j < numberOfGlyphs; j++) { - tempGroup = iSfnt->glyfGroup[j]; - if (tempGroup < TOTALTYPE) tempGroup = charGroupToIntInFile[tempGroup]; // translate old char groups - charGroup[j] = SWAPW(tempGroup); - } - break; - } - - case PRIVATE_GLIT1: - case PRIVATE_GLIT2: { - int32_t glitEntries; - sfnt_MemDataEntry *memGlit; - GlyphTableArray *src; - - glitEntries = numberOfGlyphs + GLIT_PAD; - - if (tag == PRIVATE_GLIT1) { - memGlit = this->glit1; - src = &iSfnt->asmSrc; - } else { // tag == PRIVATE_GLIT2 - memGlit = this->glit2; - src = &iSfnt->vttSrc; - } - - for (j = 0; j < numberOfGlyphs; j++) { - memGlit[j].glyphCode = (unsigned short)j; - memGlit[j].offset = src->dataPos[j]; - memGlit[j].length = src->dataPos[j+1] - src->dataPos[j]; - } - for (j = numberOfGlyphs, j2 = 0; j2 < GLIT_PAD; j++, j2++) { - memGlit[j].glyphCode = glitPadIndex[j2]; - memGlit[j].offset = src->dataPos[j]; - memGlit[j].length = (j < glitEntries-1 ? src->dataPos[j+1] : src->used) - src->dataPos[j]; - } - memGlit[numberOfGlyphs].offset = PRIVATE_STAMP_1; // Stamp it; - - this->tmpOffsetTable->table[i].length = PackGlit((sfnt_FileDataEntry *)&tmpSfnt[sfntPos],glitEntries,memGlit); - break; - } - case PRIVATE_PGM1: { - this->tmpOffsetTable->table[i].length = iSfnt->asmSrc.used; - memcpy(&tmpSfnt[sfntPos],iSfnt->asmSrc.data,iSfnt->asmSrc.used); - break; - } - case PRIVATE_PGM2: { - this->tmpOffsetTable->table[i].length = iSfnt->vttSrc.used; - memcpy(&tmpSfnt[sfntPos],iSfnt->vttSrc.data,iSfnt->vttSrc.used); - break; - } - case tag_PreProgram: { - this->tmpOffsetTable->table[i].length = this->binSize[asmPREP]; - memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmPREP],this->binSize[asmPREP]); - break; - } - case tag_FontProgram: { - this->tmpOffsetTable->table[i].length = this->binSize[asmFPGM]; - memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmFPGM],this->binSize[asmFPGM]); - break; - } - case tag_ControlValue: { - this->cvt->GetCvtBinary((int32_t*)&this->tmpOffsetTable->table[i].length,&tmpSfnt[sfntPos]); - break; - } - case PRIVATE_CVAR: - if (this->tsicBinSize) { - this->UpdatePrivateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); - } else { - memcpy((char*)&tmpSfnt[sfntPos], (char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); - } - break; - case tag_CVTVariations: - if (this->cvarBinSize) { - this->UpdateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); - } else { - memcpy((char*)&tmpSfnt[sfntPos], (char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); - } - break; - case tag_GridfitAndScanProc: - if (this->gaspBinSize) { - unsigned short *packed; - int32_t s; - - this->tmpOffsetTable->table[i].length = this->gaspBinSize; - packed = (unsigned short *)&tmpSfnt[sfntPos]; - *packed++ = SWAPW(this->gaspTable.version); - *packed++ = SWAPW(this->gaspTable.numRanges); - for (s = 0; s < this->gaspTable.numRanges; s++) { - unsigned short rangeMaxPPEM = this->gaspTable.gaspRange.at(s).rangeMaxPPEM; - unsigned short rangeGaspBehavior = this->gaspTable.gaspRange.at(s).rangeGaspBehavior; - *packed++ = SWAPW(rangeMaxPPEM); - *packed++ = SWAPW(rangeGaspBehavior); - } - } else { - memcpy((char*)&tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); - } - break; - - default: { - memcpy((char*)&tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); - break; - } - } // switch (tag) - - this->tmpOffsetTable->table[i].offset = sfntPos; - sfntPos += this->tmpOffsetTable->table[i].length; - - pad = DWordPad(sfntPos) - sfntPos; - memcpy((char*)&tmpSfnt[sfntPos],(char*)&zero,pad); // zero pad for long word alignment - sfntPos += pad; - } -#ifdef _DEBUG - assert(newSfntSizeEstimate >= sfntPos); -#endif -// once we've finished plugging in all the tables, plug in the directory (temporary offset table) - - { sfnt_DirectoryEntry *entry; - - entry = &(this->tmpOffsetTable->table[0]); - for (i=0; itmpOffsetTable->numOffsets; i++, entry++) - { - entry->tag = SWAPL(entry->tag); - entry->checkSum = SWAPL(entry->checkSum); - entry->offset = SWAPL(entry->offset); - entry->length = SWAPL(entry->length); - } - this->tmpOffsetTable->version = SWAPL(this->tmpOffsetTable->version); - this->tmpOffsetTable->numOffsets = SWAPW(this->tmpOffsetTable->numOffsets); - this->tmpOffsetTable->searchRange = SWAPW(this->tmpOffsetTable->searchRange); - this->tmpOffsetTable->entrySelector = SWAPW(this->tmpOffsetTable->entrySelector); - this->tmpOffsetTable->rangeShift = SWAPW(this->tmpOffsetTable->rangeShift); - } - memcpy((char*)tmpSfnt,(char*)this->tmpOffsetTable, headerSize); - - result = true; // by now - -term: // function termination code - if (result) { // successful so far, now swap sfntTmpHandle <-> sfntHandle and install new sfnt; swap => no resize/handle deallocation/handle copy/memory fragmentation - tmpSfntHandle = this->sfntHandle; this->sfntHandle = this->sfntTmpHandle; this->sfntTmpHandle = tmpSfntHandle; - tmpSfntSize = this->maxSfntSize; this->maxSfntSize = this->maxTmpSfntSize; this->maxTmpSfntSize = tmpSfntSize; - this->sfntSize = sfntPos; - - this->CalculateNewCheckSums(); - this->CalculateCheckSumAdjustment(); - this->SortTableDirectory(); - - // claudebe the glit entries must be updated - this->glit1Entries = this->glit2Entries = 0; - this->shortIndexToLocTable = this->outShortIndexToLocTable; - - // adjust maxGlyphs to what we've stamped in PRIVATE_GLIT1 and PRIVATE_GLIT2 above - this->maxGlyphs = numberOfGlyphs; - - result = this->UnpackGlitsLoca(errMsg) && this->UpdateMaxPointsAndContours(errMsg) && this->UnpackCharGroup(errMsg) && this->SetSfnt(-1,-1,errMsg); - } - return result; - -} // TrueTypeFont::IncrBuildNewSfnt - -bool TrueTypeFont::InitIncrBuildSfnt(bool binaryOnly, wchar_t errMsg[]) { - IncrBuildSfntData *iSfnt; - bool allocated; - - errMsg[0] = L'\0'; - - this->incrBuildSfntData = iSfnt = (IncrBuildSfntData *)NewP(sizeof(IncrBuildSfntData)); - if (iSfnt == NULL) { swprintf(errMsg,L"Failed to allocate this->incrBuildSfntData"); return false; } - - iSfnt->binaryOnly = binaryOnly; - iSfnt->numGlyphs = this->NumberOfGlyphs(); - iSfnt->currGlyph = 0; - - allocated = InitDynamicArray(&iSfnt->binary); - if (allocated && !iSfnt->binaryOnly) allocated = InitDynamicArray(&iSfnt->asmSrc) && InitDynamicArray(&iSfnt->vttSrc); - - if (!allocated) { swprintf(errMsg,L"Failed to allocate iSfnt dynamic arrays"); return false; } - - this->InitNewProfiles(); // TrueTypeFont keeps two sets of profiles, both are updated after any assembly, but only one is reset here - - return true; // by now -} // TrueTypeFont::InitIncrBuildSfnt - -void AddTextToDynArray(GlyphTableArray *dynArray, int32_t glyphIndex, TextBuffer *text) { - unsigned char *data; - size_t packedSize; - - data = &dynArray->data[dynArray->used]; - if (text != NULL) text->GetText(&packedSize,(char *)data); else packedSize = 0; - if (packedSize & 1) data[packedSize++] = '\x0d'; // CR-pad word align - dynArray->dataPos[glyphIndex] = dynArray->used; - dynArray->used += (int32_t)packedSize; -} // AddTextToDynArray - -bool TrueTypeFont::AddGlyphToNewSfnt(CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfBin, TextBuffer *glyfText, TextBuffer *talkText, wchar_t errMsg[]) { - IncrBuildSfntData *iSfnt; - bool allocated; - unsigned char *data; - int32_t packedSize; - - errMsg[0] = L'\0'; - - if (this->incrBuildSfntData == NULL) { swprintf(errMsg,L"this->incrBuildSfntData is NULL"); return false; } - iSfnt = (IncrBuildSfntData *)this->incrBuildSfntData; - - if (iSfnt->currGlyph != glyphIndex || glyphIndex < 0 || iSfnt->numGlyphs <= glyphIndex) { - swprintf(errMsg,L"Calling TrueTypeFont::AddGlyphToNewSfnt out of sequence: expected glyph %li, received glyph %li",iSfnt->currGlyph,glyphIndex); return false; - } - - iSfnt->glyfGroup[iSfnt->currGlyph] = (unsigned char)group; - - allocated = AssertDynamicArraySize(&iSfnt->binary,this->GetPackedGlyphSize(glyphIndex,glyph,glyfBinSize)); - if (allocated && !iSfnt->binaryOnly) - { - allocated = AssertDynamicArraySize(&iSfnt->asmSrc,glyfText->TheLengthInUTF8()) && AssertDynamicArraySize(&iSfnt->vttSrc,talkText->TheLengthInUTF8()); - } - - if (!allocated) - { - swprintf(errMsg,L"Failed to assert iSfnt dynamic array sizes"); - return false; - } - - iSfnt->composite[iSfnt->currGlyph] = glyph->composite; - - data = &iSfnt->binary.data[iSfnt->binary.used]; - packedSize = this->PackGlyph(data,iSfnt->currGlyph,glyph,glyfBinSize,glyfBin,&iSfnt->hmtx[iSfnt->currGlyph]); - if (packedSize & 1) data[packedSize++] = '\x00'; // zero-pad word align - iSfnt->binary.dataPos[iSfnt->currGlyph] = iSfnt->binary.used; - iSfnt->binary.used += packedSize; - - if (!iSfnt->binaryOnly) { - AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph,glyfText); - AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph,talkText); - } - - iSfnt->currGlyph++; - - return true; // by now -} // TrueTypeFont::AddGlyphToNewSfnt - -bool TrueTypeFont::TermIncrBuildSfnt(bool disposeOnly, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *fpgmText, wchar_t errMsg[]) { - // notice that it may seem inconsistent that in the above AddGlyphToNewSfnt we add both sources and binary data (of individual glyphs), - // while for TermIncrBuild we only add sources. This has to do with the weird architecture which, from the point of view of sources, - // understands prep, fpgm, and cvt as "special" glyphs at the end of the respective private table, while from the point of view of - // binary data, prep, fpgm, and cvt are individual TrueType tables. Not my first choice... - - IncrBuildSfntData *iSfnt; - bool done; - - if (this->incrBuildSfntData == NULL) { - // don't overwrite errMsg if we're merely called to "cleanup", see also calling pattern - if (!disposeOnly) swprintf(errMsg,L"this->incrBuildSfntData is NULL"); - return false; - } - - iSfnt = (IncrBuildSfntData *)this->incrBuildSfntData; - - if (disposeOnly) { - // don't return true if we're merely called to "cleanup", see also calling pattern - done = false; - } else { - done = true; // assume - errMsg[0] = L'\0'; - - if (!iSfnt->binaryOnly) { - done = AssertDynamicArraySize(&iSfnt->asmSrc,prepText->TheLengthInUTF8() + cvtText->TheLengthInUTF8() + fpgmText->TheLengthInUTF8()); - if (done) { - AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph, NULL); // dummy for future stamp - AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+1,prepText); // the sources for the prep, fpgm, cvt, and parm - AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+2,fpgmText); // must follow the same sequence as glitPadIndex!!! - AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+3,cvtText); - AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+4,NULL); - - AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph, NULL); // dummy for future stamp - AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+1,NULL); // more dummies - AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+2,NULL); - AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+3,NULL); - AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+4,NULL); - } else { - swprintf(errMsg,L"Failed to assert iSfnt dynamic array sizes"); - } - } - - - if (done) { - this->UseNewProfiles(); // copy new profiles into current ones and pack into current sfnt - done = this->IncrBuildNewSfnt(errMsg); - } - - } - - if (!iSfnt->binaryOnly) { - TermDynamicArray(&iSfnt->vttSrc); - TermDynamicArray(&iSfnt->asmSrc); - } - TermDynamicArray(&iSfnt->binary); - - DisposeP((void **)&iSfnt); - this->incrBuildSfntData = NULL; - - return done; -} // TrueTypeFont::TermIncrBuildSfnt - -sfnt_maxProfileTable TrueTypeFont::GetProfile(void) { - return this->profile; -} // TrueTypeFont::GetProfile - -int32_t TrueTypeFont::GetUnitsPerEm(void) { // FUnits Per EM (2048 is typical) - return this->unitsPerEm; -} // TrueTypeFont::GetUnitsPerEm - -void TrueTypeFont::InitNewProfiles(void) { - this->newProfile.version = 0x00010000; - this->newProfile.numGlyphs = this->profile.numGlyphs; - this->newProfile.maxPoints = 0; - this->newProfile.maxContours = 0; - this->newProfile.maxCompositePoints = 0; - this->newProfile.maxCompositeContours = 0; - - // those values shouldn't be updated in recalc 'maxp', only reset during autohinting GregH 8/23/95 - // this->newProfile.maxElements = this->profile.maxElements; - // this->newProfile.maxTwilightPoints = this->profile.maxTwilightPoints; - // this->newProfile.maxStorage = this->profile.maxStorage; - - // as per ClaudeBe 6/30/97 we can safely assume these minima - this->newProfile.maxElements = Max(MIN_ELEMENTS,this->profile.maxElements); - this->newProfile.maxTwilightPoints = Max(MIN_TWILIGHTPOINTS,this->profile.maxTwilightPoints); - this->newProfile.maxStorage = Max(MIN_STORAGE,this->profile.maxStorage); - - this->newProfile.maxFunctionDefs = this->profile.maxFunctionDefs; - this->newProfile.maxInstructionDefs = this->profile.maxInstructionDefs; - this->newProfile.maxStackElements = 0; - this->newProfile.maxSizeOfInstructions = 0; - this->newProfile.maxComponentElements = 0; - this->newProfile.maxComponentDepth = 0; - // see comments in TrueTypeFont::UpdateAssemblerProfile - for (int32_t i = 0; i < Len(this->maxStackElements); i++) this->maxStackElements[i] = 0; - - this->newMetricProfile.xMin = 0x7fff; - this->newMetricProfile.yMin = 0x7fff; - this->newMetricProfile.xMax = (short)0x8000; - this->newMetricProfile.yMax = (short)0x8000; - - this->newMetricProfile.advanceWidthMax = (short)0x8000; - this->newMetricProfile.minLeftSideBearing = (short)0x7fff; - this->newMetricProfile.minRightSideBearing = 0x7fff; - this->newMetricProfile.xMaxExtent = (short)0x8000; -} // TrueTypeFont::InitNewProfiles - -void TrueTypeFont::InheritProfiles(void) -{ - this->newProfile = this->profile; -} - -void TrueTypeFont::UseNewProfiles(void) { - - this->profile = this->newProfile; - this->metricProfile = this->newMetricProfile; - this->PackMaxpHeadHhea(); -} // TrueTypeFont::UseNewProfiles - -void TrueTypeFont::UpdateGlyphProfile(TrueTypeGlyph *glyph) { - short numberOfPoints = glyph->numContoursInGlyph ? glyph->endPoint[glyph->numContoursInGlyph-1] + 1 : 0; - - if (!glyph->composite) { // cf. TT specs, "maxp": Max points/contours in a non-composite glyph - this->profile.maxPoints = Max(this->profile.maxPoints,numberOfPoints); - this->newProfile.maxPoints = Max(this->newProfile.maxPoints,numberOfPoints); - this->profile.maxContours = Max(this->profile.maxContours,(short)glyph->numContoursInGlyph); - this->newProfile.maxContours = Max(this->newProfile.maxContours,(short)glyph->numContoursInGlyph); - } -} // TrueTypeFont::UpdateGlyphProfile - -void TrueTypeFont::UpdateMetricProfile(TrueTypeGlyph *glyph) { - short end = glyph->numContoursInGlyph > 0 ? glyph->endPoint[glyph->numContoursInGlyph-1] : -1, - advanceWidth = glyph->x[end + PHANTOMPOINTS] - glyph->x[end + PHANTOMPOINTS - 1]; // get whatever the rasterizer has deemed correct in terms of composite and useMyMetrics - - this->metricProfile.xMin = Min(this->metricProfile.xMin, glyph->xmin); - this->newMetricProfile.xMin = Min(this->newMetricProfile.xMin,glyph->xmin); - this->metricProfile.xMax = Max(this->metricProfile.xMax, glyph->xmax); - this->newMetricProfile.xMax = Max(this->newMetricProfile.xMax,glyph->xmax); - this->metricProfile.yMin = Min(this->metricProfile.yMin, glyph->ymin); - this->newMetricProfile.yMin = Min(this->newMetricProfile.yMin,glyph->ymin); - this->metricProfile.yMax = Max(this->metricProfile.yMax, glyph->ymax); - this->newMetricProfile.yMax = Max(this->newMetricProfile.yMax,glyph->ymax); - - this->metricProfile.minLeftSideBearing = Min(this->metricProfile.minLeftSideBearing, glyph->xmin); - this->newMetricProfile.minLeftSideBearing = Min(this->newMetricProfile.minLeftSideBearing, glyph->xmin); - this->metricProfile.minRightSideBearing = Min(this->metricProfile.minRightSideBearing, advanceWidth - glyph->xmax); - this->newMetricProfile.minRightSideBearing = Min(this->newMetricProfile.minRightSideBearing,advanceWidth - glyph->xmax); - this->metricProfile.advanceWidthMax = Max(this->metricProfile.advanceWidthMax, advanceWidth); - this->newMetricProfile.advanceWidthMax = Max(this->newMetricProfile.advanceWidthMax, advanceWidth); - this->metricProfile.xMaxExtent = Max(this->metricProfile.xMaxExtent, glyph->xmax); - this->newMetricProfile.xMaxExtent = Max(this->newMetricProfile.xMaxExtent, glyph->xmax); -} // TrueTypeFont::UpdateMetricProfile - -void TrueTypeFont::UpdateAssemblerProfile(ASMType asmType, short maxFunctionDefs, short maxStackElements, short maxSizeOfInstructions) { - uint16 maxStackElementsPrepFpgm,maxStackElementsGlyfFpgm; - - this->profile.maxFunctionDefs = Max(this->profile.maxFunctionDefs,maxFunctionDefs); - this->newProfile.maxFunctionDefs = Max(this->newProfile.maxFunctionDefs,maxFunctionDefs); - - this->profile.maxStackElements = Max(this->profile.maxStackElements,maxStackElements); // <--- relevant during individual assembly - - // Coming up with a good max value for the number of stack elements used at any one time - // is a bit of a heuristic, because we cannot possibly test all glyphs against all sizes - // and all combinations of rendering modes (black-and-white, grey-scaled, many versions - // of cleartype) and all variants of the rasterizer. If there were no branches, loops, or - // calls, then it would be straightforward to determine an upper limit for the fpgm (or - // any function therein), for the prep, and for the maximum of all upper limits for any - // of the glyfs. For branches, the argument could be made that each branch will have to - // affect the stack depth in the same way, else the respective code will terminate with - // an imbalance (stack underflow, or extra stack elements left over). To account for that - // we can safely take the maximum of all branches (and recursively for branches within - // branches). For loops theoretically each iteration could grow the stack, and the number - // of iterations need not be known by the assembler, so this could theoretically lead to - // unbound growth. This is a much less likely scenario though because I have not seen a - // great many fonts with iterations, and the described scenario would again be an erro- - // neous piece of code. Function calls, in simple cases, typically do not call other - // functions, so the safe upper limit would be to take the deepest stack of any glyf and - // add to that the deepest stack resulting from any function call, or the maximum depth - // resulting from executing the prep and add to that the deepest stack resulting from any - // function call. Calls within calls, and much less likely, recursive calls, would again - // falsify this assumption, but bar testing the whole matrix mentioned above, we cannot - // think of a better solution at this time. Notice, finally, that we assume that the - // TTAssembler, which produces values for maxStackElements upon assembling the fpgm, - // prep, or any of the glyfs, chooses the smartest strategy it can, but we didn't verify - // this assumption because the code is a little bit too chaotic to understand what it - // really does... - - // new heuristic for re-calc maxp or compiling/assembling everything for all glyphs only!!! - - // notice that in order to handle this->profile.maxStackElements in much the same way as - // this->newProfile.maxStackElements, we'd have to store maxp values for maxStackElements - // separately, for each of the fpgm, prep, and glyf. Since historically we don't, what - // comes in when opening a font is a this->profile.maxStackElements that is either updated - // according to the old method, or it is a max of a sum of parts we don't know anymore. - // Therefore this is the best we can do. Any problems produced by the incremental method - // (such as after a newly assembled glyf) can always be corrected by re-calculating the - // maxp from scratch. - - this->maxStackElements[asmType] = Max(this->maxStackElements[asmType],maxStackElements); - // from both the deepest level in the prep or any glyf we could call a function, hence add the respective maxima - maxStackElementsPrepFpgm = this->maxStackElements[asmPREP] + this->maxStackElements[asmFPGM]; - maxStackElementsGlyfFpgm = this->maxStackElements[asmGLYF] + this->maxStackElements[asmFPGM]; - // the max stack depth then becomes the maximum of the two intermediate maxima above - this->newProfile.maxStackElements = Max(maxStackElementsPrepFpgm,maxStackElementsGlyfFpgm); // <--- relevant during complete assembly/re-calc maxp - - this->profile.maxSizeOfInstructions = Max(this->profile.maxSizeOfInstructions,maxSizeOfInstructions); - this->newProfile.maxSizeOfInstructions = Max(this->newProfile.maxSizeOfInstructions,maxSizeOfInstructions); -} // TrueTypeFont::UpdateAssemblerProfile - -void TrueTypeFont::UpdateAutohinterProfile(short maxElements, short maxTwilightPoints, short maxStorage) { - // for autohinter pre-program - this->profile.maxFunctionDefs = 0; - this->newProfile.maxFunctionDefs = 0; - - this->profile.maxInstructionDefs = 0; - this->newProfile.maxInstructionDefs = 0; - - this->profile.maxElements = maxElements; - this->newProfile.maxElements = maxElements; - - this->profile.maxTwilightPoints = maxTwilightPoints; - this->newProfile.maxTwilightPoints = maxTwilightPoints; - - this->profile.maxStorage = maxStorage; - this->newProfile.maxStorage = maxStorage; -} // TrueTypeFont::UpdateAutohinterProfile - -bool TrueTypeFont::HasSource() -{ - return (this->glit1Entries != 0) && (this->glit2Entries != 0); -} - -bool TrueTypeFont::ReverseInterpolateCvarTuples() -{ - bool result = false; - - if (!this->IsVariationFont()) - return true; // all done - - - auto instanceManager = this->GetInstanceManager(); - auto cvarTuples = instanceManager->GetCvarTuples(); - - std::vector defaultCvts; - this->GetDefaultCvts(defaultCvts); - - // Iterate through tuples - for (uint16_t index = 0; index < cvarTuples->size(); ++index) - { - auto& editedCvtsFromTuple = cvarTuples->at(index)->editedCvtValues; - auto& interpolatedCvtsFromTuple = cvarTuples->at(index)->interpolatedCvtValues; - - // Assert size of editedCvtValue with defaultCvt size - editedCvtsFromTuple.resize(defaultCvts.size(), Variation::EditedCvtValue()); - // Initialize interpolatedCvtValues with defaultCvt values - interpolatedCvtsFromTuple.resize(defaultCvts.size(), Variation::InterpolatedCvtValue()); - for (size_t cvt = 0; cvt < defaultCvts.size(); ++cvt) - { - interpolatedCvtsFromTuple[cvt] = Variation::InterpolatedCvtValue(defaultCvts[cvt]); - } - - for (size_t cvt = 0; cvt < defaultCvts.size(); cvt++) - { - if (editedCvtsFromTuple.at(cvt).Edited()) - { - interpolatedCvtsFromTuple[cvt].Update(editedCvtsFromTuple.at(cvt).Value()); - } - } - - } - - result = cvtVariationInterpolator_->ReverseInterpolate(defaultCvts, axisCount_, *cvarTuples); - - return result; -} - - -template -bool almost_equal(T x, T y, int ulp) -{ - return std::abs(x - y) <= ulp || std::abs(x + y) <= ulp; -} - -#define ULP 2 -#define FLAGS_CANT_CHANGE_VAR (ARG_1_AND_2_ARE_WORDS | ARGS_ARE_XY_VALUES | ROUND_XY_TO_GRID | WE_HAVE_A_SCALE | MORE_COMPONENTS | WE_HAVE_AN_X_AND_Y_SCALE | WE_HAVE_A_TWO_BY_TWO | WE_HAVE_A_TWO_BY_TWO) - -CheckCompositeResult CheckCompositeVariationCompatible(const short* pFirst, short firstSize, const short* pSecond, short secondSize) -{ - TrueTypeCompositeComponent first, second; - int i1 = 0, i2 = 0; - - if (pFirst == nullptr || pSecond == nullptr || firstSize != secondSize) - return CheckCompositeResult::Fail; - - do { - const unsigned short noChangeFlags = FLAGS_CANT_CHANGE_VAR; - first.flags = pFirst[i1++]; first.flags = SWAPW(first.flags); - second.flags = pSecond[i2++]; second.flags = SWAPW(second.flags); - - if ((first.flags & noChangeFlags) != (second.flags & noChangeFlags)) - return CheckCompositeResult::Fail; - - first.glyphIndex = pFirst[i1++]; first.glyphIndex = SWAPW(first.glyphIndex); - second.glyphIndex = pSecond[i2++]; second.glyphIndex = SWAPW(second.glyphIndex); - - if (first.glyphIndex != second.glyphIndex) - return CheckCompositeResult::Fail; - - if (first.flags & ARG_1_AND_2_ARE_WORDS) - { - first.arg1 = pFirst[i1++]; first.arg1 = SWAPW(first.arg1); - second.arg1 = pSecond[i2++]; second.arg1 = SWAPW(second.arg1); - - first.arg2 = pFirst[i1++]; first.arg2 = SWAPW(first.arg2); - second.arg2 = pSecond[i2++]; second.arg2 = SWAPW(second.arg2); - - if (first.arg1 != second.arg1 || first.arg2 != second.arg2) - return CheckCompositeResult::Fail; - } - else - { - // both args are packed in a uint16 so treat the same since we only compare about comparison - first.arg1 = pFirst[i1++]; - second.arg1 = pSecond[i2++]; - - if (first.arg1 != second.arg1) - return CheckCompositeResult::Fail; - } - - if (first.flags & WE_HAVE_A_SCALE) - { - first.xscale = pFirst[i1++]; first.xscale = SWAPW(first.xscale); - second.xscale = pSecond[i2++]; second.xscale = SWAPW(second.xscale); - - if (first.xscale == second.xscale) - return CheckCompositeResult::Success; - else if (almost_equal(first.xscale, second.xscale, ULP)) - return CheckCompositeResult::Tolerance; - else return CheckCompositeResult::Fail; - } - else if (first.flags & WE_HAVE_AN_X_AND_Y_SCALE) - { - first.xscale = pFirst[i1++]; first.xscale = SWAPW(first.xscale); - second.xscale = pSecond[i2++]; second.xscale = SWAPW(second.xscale); - - first.yscale = pFirst[i1++]; first.yscale = SWAPW(first.yscale); - second.yscale = pSecond[i2++]; second.yscale = SWAPW(second.yscale); - - if (first.xscale == second.xscale && first.yscale == second.yscale) - return CheckCompositeResult::Success; - else if(almost_equal(first.xscale, second.xscale, ULP) && almost_equal(first.yscale, second.yscale, ULP)) - return CheckCompositeResult::Tolerance; - else return CheckCompositeResult::Fail; - } - else if (first.flags & WE_HAVE_A_TWO_BY_TWO) - { - first.xscale = pFirst[i1++]; first.xscale = SWAPW(first.xscale); - second.xscale = pSecond[i2++]; second.xscale = SWAPW(second.xscale); - - first.scale01 = pFirst[i1++]; first.scale01 = SWAPW(first.scale01); - second.scale01 = pSecond[i2++]; second.scale01 = SWAPW(second.scale01); - - first.scale10 = pFirst[i1++]; first.scale10 = SWAPW(first.scale10); - second.scale10 = pSecond[i2++]; second.scale10 = SWAPW(second.scale10); - - first.yscale = pFirst[i1++]; first.yscale = SWAPW(first.yscale); - second.yscale = pSecond[i2++]; second.yscale = SWAPW(second.yscale); - - if (first.xscale == second.xscale && first.yscale == second.yscale && first.scale01 == second.scale01 && first.scale10 == second.scale10) - return CheckCompositeResult::Success; - else if (almost_equal(first.xscale, second.xscale, ULP) && almost_equal(first.yscale, second.yscale, ULP) && almost_equal(first.scale01, second.scale01, ULP) && almost_equal(first.scale10, second.scale10, ULP)) - return CheckCompositeResult::Tolerance; - else return CheckCompositeResult::Fail; - } - - } while (first.flags & MORE_COMPONENTS); - - assert(i1 == i2); - assert(i1 <= firstSize); - - return CheckCompositeResult::Success; -} - -/* - * update the contenents in the glyph structure: glyph->componentData - * startOfLinePtr : for error spot indication - * context = CO_CompInstrFollow if more Composite line followed; - * context = CO_StdInstrFollow if more instructions followed; - * context = CO_NothingFollows otherwise; - * round : boolean variable [r] or [R]; - * name : instruction name. - * args : arguments storage - * argc : number of arguments. - * return "true" if sucessful, otherwise return "false" - */ - -void TrueTypeFont::UpdateCompositeProfile(TrueTypeGlyph *glyph, TTCompositeProfile *compositeProfile, short context, short RoundingCode, short InstructionIndex, short *args, short argc, sfnt_glyphbbox *Newbbox, short *error) { - short i, tmpShort,flags = 0, contours, points; - unsigned short glyphIndex; - - short ComponentDepth; - sfnt_glyphbbox sub_bbox, tmpbbox; - - - glyph->ComponentVersionNumber = -1; /* Magic number */ - - if ( compositeProfile->nextExitOffset ) { - *error = co_AnchorNothingAbove; - return; - } - - - switch (InstructionIndex ) { - case OVERLAPIndex: - compositeProfile->GlobalNON_OVERLAPPING = 0; - if ( context != CO_CompInstrFollow ) *error = co_OverlapLastInstruction; - return; - break; - case NONOVERLAPIndex: - compositeProfile->GlobalNON_OVERLAPPING = 1; - if ( context != CO_CompInstrFollow ) *error = co_NonOverlapLastInstruction; - return; - break; - case USEMYMETRICSIndex: - compositeProfile->GlobalUSEMYMETRICS = 1; - if ( context != CO_CompInstrFollow ) *error = co_UseMymetricsLastInstruction; - return; - break; - case SCALEDCOMPONENTOFFSETIndex: - compositeProfile->GlobalSCALEDCOMPONENTOFFSET = 1; - if ( context != CO_CompInstrFollow ) *error = co_ScaledComponentOffsetLastInstruction; - if ( compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET ) *error = co_UnscaledComponentOffsetAlreadySet; - return; - break; - case UNSCALEDCOMPONENTOFFSETIndex: - compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET = 1; - if ( context != CO_CompInstrFollow ) *error = co_UnscaledComponentOffsetLastInstruction; - if ( compositeProfile->GlobalSCALEDCOMPONENTOFFSET ) *error = co_ScaledComponentOffsetAlreadySet; - return; - break; - - case ANCHORIndex: - case SANCHORIndex: - if (!this->GetNumberOfPointsAndContours(args[0], &contours, &points, &ComponentDepth,&sub_bbox)) { - *error = co_ComponentSizeOverflow; - return; - } - if ( args[1] > compositeProfile->anchorPrevPoints || args[2] > points ) { - *error = co_AnchorArgExceedMax; - return; - } - break; - } - - - // if ( compositeProfile->GlobalNON_OVERLAPPING ) flags |= NON_OVERLAPPING; else flags &= (~NON_OVERLAPPING); - // per Apple's and Microsoft's specs, and per GregH (27 Jan 2003): this flag should always be set to 0 (zero) - flags &= (~NON_OVERLAPPING); - if ( compositeProfile->GlobalUSEMYMETRICS ) flags |= USE_MY_METRICS; else flags &= (~USE_MY_METRICS); - if ( compositeProfile->GlobalUSEMYMETRICS ) compositeProfile->GlobalUSEMYMETRICS = 0; - if ( compositeProfile->GlobalSCALEDCOMPONENTOFFSET ) flags |= SCALED_COMPONENT_OFFSET; - if ( compositeProfile->GlobalSCALEDCOMPONENTOFFSET ) compositeProfile->GlobalSCALEDCOMPONENTOFFSET = 0; - if ( compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET ) flags |= UNSCALED_COMPONENT_OFFSET; - if ( compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET ) compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET = 0; - - if (InstructionIndex == OFFSETIndex || InstructionIndex == SOFFSETIndex) - { - flags |= ARGS_ARE_XY_VALUES; - for ( i = 1; i <= 2; i++ ) { - if ( args[i] < -128 || args[i] > 127 ) { - flags |= ARG_1_AND_2_ARE_WORDS; - break; - } - } - } else { - for ( i = 1; i <= 2; i++ ) { - if ( args[i] < 0 || args[i] > 255 ) { - flags |= ARG_1_AND_2_ARE_WORDS; - break; - } - } - } - - if ( RoundingCode ) flags |= ROUND_XY_TO_GRID; - - - - if ( argc == 7 ) { - /* the ConvertDoubleToShort conversion is done now during parsing */ - if ( args[4] == 0 && args[5] == 0) { - if ( args[3] == args[6] ) { - flags |= WE_HAVE_A_SCALE; - } else { - flags |= WE_HAVE_AN_X_AND_Y_SCALE; - } - } else { - flags |= WE_HAVE_A_TWO_BY_TWO; - } - } - - if ( context == CO_CompInstrFollow ) flags |= MORE_COMPONENTS; else flags &= (~MORE_COMPONENTS); - if ( context == CO_StdInstrFollow ) { - flags |= WE_HAVE_INSTRUCTIONS; - compositeProfile->nextExitOffset = true; - } - - if (compositeProfile->GlobalMORE_COMPONENTS) { - compositeProfile->numberOfCompositeElements++; - glyph->componentData[ glyph->componentSize++] = SWAPW(flags); - - glyphIndex = (unsigned short)args[0]; - glyph->componentData[ glyph->componentSize++] = SWAPW(glyphIndex); - if ( flags & ARG_1_AND_2_ARE_WORDS ) { - /* arg 1 and 2 */ - glyph->componentData[ glyph->componentSize++] = SWAPW(args[1]); - glyph->componentData[ glyph->componentSize++] = SWAPW(args[2]); - } else { - /* arg 1 and 2 as bytes */ - unsigned char *uByte = (unsigned char *)&glyph->componentData[glyph->componentSize]; - - uByte[0] = (unsigned char)args[1]; - uByte[1] = (unsigned char)args[2]; - glyph->componentSize++; - } - /* scale */ - if ( flags & WE_HAVE_A_SCALE ) { - glyph->componentData[ glyph->componentSize++] = SWAPW(args[3]); - } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { - glyph->componentData[ glyph->componentSize++] = SWAPW(args[3]); - glyph->componentData[ glyph->componentSize++] = SWAPW(args[6]); - } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) { - /* xscale, scale01, scale10, yscale */ - glyph->componentData[ glyph->componentSize++] = SWAPW(args[3]); - glyph->componentData[ glyph->componentSize++] = SWAPW(args[4]); - glyph->componentData[ glyph->componentSize++] = SWAPW(args[5]); - glyph->componentData[ glyph->componentSize++] = SWAPW(args[6]); - } - if ( glyph->componentSize >= MAXCOMPONENTSIZE ) { - *error = co_ComponentSizeOverflow; - return; - } - - this->GetNumberOfPointsAndContours(glyphIndex, &contours, &points, &ComponentDepth,&sub_bbox ); - /* claudebe, update the bounding box, inspired by sfnaccs.c, ReadComponentData */ - - if (flags & (WE_HAVE_A_SCALE | WE_HAVE_AN_X_AND_Y_SCALE | WE_HAVE_A_TWO_BY_TWO)) - { - if (flags & WE_HAVE_A_TWO_BY_TWO) - { - - if ( args[3] == 0 && args[6] == 0) { /* we have a 90degree rotation, we can compute the new bbox just using the old one */ - - tmpbbox = sub_bbox; - sub_bbox.xmin = (short) ( ((int32_t)tmpbbox.ymin * (int32_t)args[5]) >> 14); - sub_bbox.xmax = (short) ( ((int32_t)tmpbbox.ymax * (int32_t)args[5]) >> 14); - sub_bbox.ymin = (short) ( ((int32_t)tmpbbox.xmin * (int32_t)args[4]) >> 14); - sub_bbox.ymax = (short) ( ((int32_t)tmpbbox.xmax * (int32_t)args[4]) >> 14); - - if (sub_bbox.xmin > sub_bbox.xmax) { /* there was a negative scaling */ - tmpShort = sub_bbox.xmin; - sub_bbox.xmin = sub_bbox.xmax; - sub_bbox.xmax = tmpShort; - } - if (sub_bbox.ymin > sub_bbox.ymax) { /* there was a negative scaling */ - tmpShort = sub_bbox.ymin; - sub_bbox.ymin = sub_bbox.ymax; - sub_bbox.ymax = tmpShort; - } - - /* first scale, then apply the offset */ - sub_bbox.xmin += args[1]; - sub_bbox.xmax += args[1]; - sub_bbox.ymin += args[2]; - sub_bbox.ymax += args[2]; - - Newbbox->xmin = Min(Newbbox->xmin,sub_bbox.xmin); - Newbbox->ymin = Min(Newbbox->ymin,sub_bbox.ymin); - Newbbox->xmax = Max(Newbbox->xmax,sub_bbox.xmax); - Newbbox->ymax = Max(Newbbox->ymax,sub_bbox.ymax); - - } else { -// edit_ErrorMessage( L"Warning, DovMan doesn't know how to compute the glyph bounding box when rotating a glyph", glyph->componentSize ); - } - - } - else - { - /* If we have a scale factor */ - - - if (flags & WE_HAVE_AN_X_AND_Y_SCALE) - { - sub_bbox.xmin = (short) ( ((int32_t)sub_bbox.xmin * (int32_t)args[3]) >> 14); - sub_bbox.xmax = (short) ( ((int32_t)sub_bbox.xmax * (int32_t)args[3]) >> 14); - sub_bbox.ymin = (short) ( ((int32_t)sub_bbox.ymin * (int32_t)args[6]) >> 14); - sub_bbox.ymax = (short) ( ((int32_t)sub_bbox.ymax * (int32_t)args[6]) >> 14); - } - else - { - sub_bbox.xmin = (short) ( ((int32_t)sub_bbox.xmin * (int32_t)args[3]) >> 14); - sub_bbox.xmax = (short) ( ((int32_t)sub_bbox.xmax * (int32_t)args[3]) >> 14); - sub_bbox.ymin = (short) ( ((int32_t)sub_bbox.ymin * (int32_t)args[3]) >> 14); - sub_bbox.ymax = (short) ( ((int32_t)sub_bbox.ymax * (int32_t)args[3]) >> 14); - } - - if (sub_bbox.xmin > sub_bbox.xmax) { /* there was a negative scaling */ - tmpShort = sub_bbox.xmin; - sub_bbox.xmin = sub_bbox.xmax; - sub_bbox.xmax = tmpShort; - } - if (sub_bbox.ymin > sub_bbox.ymax) { /* there was a negative scaling */ - tmpShort = sub_bbox.ymin; - sub_bbox.ymin = sub_bbox.ymax; - sub_bbox.ymax = tmpShort; - } - - /* first scale, then apply the offset */ - sub_bbox.xmin += args[1]; - sub_bbox.xmax += args[1]; - sub_bbox.ymin += args[2]; - sub_bbox.ymax += args[2]; - - Newbbox->xmin = Min(Newbbox->xmin,sub_bbox.xmin); - Newbbox->ymin = Min(Newbbox->ymin,sub_bbox.ymin); - Newbbox->xmax = Max(Newbbox->xmax,sub_bbox.xmax); - Newbbox->ymax = Max(Newbbox->ymax,sub_bbox.ymax); - } - } else { - - if (flags & ARGS_ARE_XY_VALUES) - { - /* I don't neet to test ARG_1_AND_2_ARE_WORDS as I can get the arguments directely from args[] */ - sub_bbox.xmin += args[1]; - sub_bbox.xmax += args[1]; - sub_bbox.ymin += args[2]; - sub_bbox.ymax += args[2]; - - Newbbox->xmin = Min(Newbbox->xmin,sub_bbox.xmin); - Newbbox->ymin = Min(Newbbox->ymin,sub_bbox.ymin); - Newbbox->xmax = Max(Newbbox->xmax,sub_bbox.xmax); - Newbbox->ymax = Max(Newbbox->ymax,sub_bbox.ymax); - } - else - { - /* ClaudeBe, I'm not handling the anchor point case now */ - sub_bbox.xmin = sub_bbox.xmin; /* to set a break point */ - } - } - - compositeProfile->anchorPrevPoints += points; - compositeProfile->numberOfCompositeContours += contours; - compositeProfile->numberOfCompositePoints += points; - if ( compositeProfile->numberOfCompositeContours > this->profile.maxCompositeContours ) { - this->profile.maxCompositeContours = compositeProfile->numberOfCompositeContours; - } - if ( compositeProfile->numberOfCompositeContours > this->newProfile.maxCompositeContours ) { - this->newProfile.maxCompositeContours = compositeProfile->numberOfCompositeContours; - } - if ( compositeProfile->numberOfCompositePoints > this->profile.maxCompositePoints ) { - this->profile.maxCompositePoints = compositeProfile->numberOfCompositePoints; - } - if ( compositeProfile->numberOfCompositePoints > this->newProfile.maxCompositePoints ) { - this->newProfile.maxCompositePoints = compositeProfile->numberOfCompositePoints; - } - if ( compositeProfile->numberOfCompositeElements > this->profile.maxComponentElements ) { - this->profile.maxComponentElements = compositeProfile->numberOfCompositeElements; - } - if ( compositeProfile->numberOfCompositeElements > this->newProfile.maxComponentElements ) { - this->newProfile.maxComponentElements = compositeProfile->numberOfCompositeElements; - } - if ( ComponentDepth+1 > this->profile.maxComponentDepth ) { - this->profile.maxComponentDepth = ComponentDepth+1; - } - if ( ComponentDepth+1 > this->newProfile.maxComponentDepth ) { - this->newProfile.maxComponentDepth = ComponentDepth+1; - } - - } - compositeProfile->GlobalMORE_COMPONENTS = (flags & MORE_COMPONENTS ); - - return; -} // TrueTypeFont::UpdateCompositeProfile - -void TrueTypeFont::CalculateNewCheckSums(void) { -// for each table in font, calculate table's checksum - unsigned char *sfnt = this->sfntHandle; - sfnt_OffsetTable *directory; - sfnt_DirectoryEntry *entry; - unsigned short numTables; - uint32_t *loopStart,checkSum,*loopEnd,*loop,i, length; - sfnt_FontHeader *head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); - - head->checkSumAdjustment = 0; // checksum of head table to be computed with this set to 0 - directory = (sfnt_OffsetTable*)sfnt; - numTables = SWAPW(directory->numOffsets); - for (i = 0; i < numTables; i++) { - entry = &directory->table[i]; - loopStart = (uint32_t *)(sfnt + SWAPL(entry->offset)); - length = SWAPL(entry->length); - loopEnd = loopStart + DWordPad(length)/sizeof(uint32_t); - checkSum = 0; - for (loop = loopStart; loop < loopEnd; loop++) - { - checkSum += SWAPL(*loop); - }; - entry->checkSum = SWAPL(checkSum); - } -} // TrueTypeFont::CalculateNewCheckSums - -void TrueTypeFont::CalculateCheckSumAdjustment(void) { - - unsigned char *sfnt = this->sfntHandle; - sfnt_FontHeader *head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); - uint32_t *loopStart,checkSum,*loopEnd,*loop; - - loopStart = (uint32_t *)sfnt; - loopEnd = loopStart + DWordPad(this->sfntSize)/sizeof(uint32_t); - for (loop = loopStart, checkSum = 0; loop < loopEnd; loop++) - { - checkSum += SWAPL(*loop); - } - head->checkSumAdjustment = CSWAPL(0xb1b0afba - checkSum); -} // TrueTypeFont::CalculateCheckSumAdjustment - -void TrueTypeFont::SortTableDirectory(void) { - - sfnt_OffsetTable *directory = (sfnt_OffsetTable *)this->sfntHandle; - sfnt_DirectoryEntry *entry = directory->table,temp; - unsigned short numTables = SWAPW(directory->numOffsets); - int32_t i,j; - bool swap; - - swap = true; - for (i = numTables - 1; i > 0 && swap; i--) { // Bubble-Sort - swap = false; - for (j = 0; j < i; j++) { - if (SWAPL(entry[j].tag) > SWAPL(entry[j+1].tag)) { - swap = true; - temp = entry[j]; - entry[j] = entry[j+1]; - entry[j+1] = temp; - } - } - } -} // TrueTypeFont::SortTableDirectory - -void TrueTypeFont::PackMaxpHeadHhea(void) { - sfnt_FontHeader *head; - sfnt_HorizontalHeader *hhea; - - PackMaxp(this->GetTablePointer(tag_MaxProfile),&this->profile); - - head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); - head->macStyle = SWAPW(this->macStyle); - - head->xMin = SWAPW(this->metricProfile.xMin); - head->yMin = SWAPW(this->metricProfile.yMin); - head->xMax = SWAPW(this->metricProfile.xMax); - head->yMax = SWAPW(this->metricProfile.yMax); - - hhea = (sfnt_HorizontalHeader *)this->GetTablePointer(tag_HoriHeader); - - hhea->advanceWidthMax = SWAPW(this->metricProfile.advanceWidthMax); - hhea->minLeftSideBearing = SWAPW(this->metricProfile.minLeftSideBearing); - hhea->minRightSideBearing = SWAPW(this->metricProfile.minRightSideBearing); - hhea->xMaxExtent = SWAPW(this->metricProfile.xMaxExtent); -} // TrueTypeFont::PackMaxpHeadHhea - -uint32_t TrueTypeFont::GetPackedGlyphsSizeEstimate(TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc) { -// for the purpose of asserting a big enough buffer (this->sfntHandle) it should be enough to determine a -// reasonnable upper limit. Between two invocations of BuildNewSfnt, at most one glyph is changed. The -// size of this change is calculated exactly. For all other glyphs we use the current size (difference -// of position in oldIndexToLoca), regardless of the fact that the font may actually be stripped of its -// code in the process of building the new sfnt - - uint32_t length,size; - int32_t i,numEntries; //numContours; - - numEntries = this->NumberOfGlyphs(); - - size = 0; - for (i = 0; i < numEntries; i++) { - if (i == glyphIndex) { - length = this->GetPackedGlyphSize(glyphIndex,glyph,this->binSize[asmGLYF]); - } else { - length = oldIndexToLoc[i+1] - oldIndexToLoc[i]; - } - - size += length; - if (size & 1) size++; // zero-pad word align - } - return size; -} // TrueTypeFont::GetPackedGlyphsSizeEstimate - -uint32_t TrueTypeFont::GetPackedGlyphSize(int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize) { - uint32_t size; - short i, numberOfPoints, x, y, delta, j; - unsigned char bitFlags; - - size = 0; - - if (glyph->numContoursInGlyph == 0 || (glyph->numContoursInGlyph == 1 && glyph->startPoint[0] == 0 && glyph->endPoint[0] == 0 && glyph->x[0] == 0 && glyph->y[0] == 0 && !glyph->onCurve[0] && glyfBinSize == 0)) { - return size; // assume this is a dummy point that is neither instructed nor serves as an anchor, hence turn into an empty glyph by leaving with size == 0... - } - - // by now, we have at least one control point - numberOfPoints = glyph->endPoint[glyph->numContoursInGlyph - 1] + 1; // excluding the side bearing points - - if ( glyph->componentSize > 0 ) { - - size += 5*sizeof(short) + sizeof(short)*glyph->componentSize; // component version number, bbox - if (glyfBinSize > 0) size += sizeof(short) + glyfBinSize; // glyfBinSize - - } else { - - size += 5*sizeof(short) + glyph->numContoursInGlyph*sizeof(short) + sizeof(short) + glyfBinSize; // number of contours, bbox, glyfBinSize - - // Calculate flags - for ( x = y = i = 0; i < numberOfPoints; i++ ) { - bitFlags = glyph->onCurve[i] & ONCURVE; - delta = glyph->x[i] - x; x = glyph->x[i]; - if ( delta == 0 ) { - bitFlags |= NEXT_X_IS_ZERO; - } else if ( delta >= -255 && delta <= 255 ) { - bitFlags |= XSHORT; - if ( delta > 0 ) - bitFlags |= SHORT_X_IS_POS; - } - - delta = glyph->y[i] - y; y = glyph->y[i]; - if ( delta == 0 ) { - bitFlags |= NEXT_Y_IS_ZERO; - } else if ( delta >= -255 && delta <= 255 ) { - bitFlags |= YSHORT; - if ( delta > 0 ) - bitFlags |= SHORT_Y_IS_POS; - } - this->tmpFlags[i] = bitFlags; - } - - // Write out bitFlags - for ( i = 0; i < numberOfPoints;) { - unsigned char repeat = 0; - for (j = i+1; j < numberOfPoints && this->tmpFlags[i] == this->tmpFlags[j] && repeat < 255; j++) - repeat++; - if (repeat > 1) { - size += 2; - i = j; - } else { - size++; - i++; - } - } - - // Write out X - for (x = i = 0; i < numberOfPoints; i++) { - delta = glyph->x[i] - x; x = glyph->x[i]; - if (this->tmpFlags[i] & XSHORT) { - if (!(this->tmpFlags[i] & SHORT_X_IS_POS)) delta = -delta; - size++; - } else if (!(this->tmpFlags[i] & NEXT_X_IS_ZERO)) - size += 2; - } - - // Write out Y - for (y = i = 0; i < numberOfPoints; i++) { - delta = glyph->y[i] - y; y = glyph->y[i]; - if (this->tmpFlags[i] & YSHORT) { - if (!(this->tmpFlags[i] & SHORT_Y_IS_POS)) delta = -delta; - size++; - } else if (!(this->tmpFlags[i] & NEXT_Y_IS_ZERO)) - size += 2; - } - } - - return size; // by now -} // TrueTypeFont::GetPackedGlyphSize - -uint32_t TrueTypeFont::PackGlyphs(StripCommand strip, TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc, uint32_t *newIndexToLoc, unsigned char *dst) { - uint32_t length,size; - int32_t i,numEntries; //numContours; - unsigned char *src; - - numEntries = this->NumberOfGlyphs(); - - src = this->GetTablePointer(tag_GlyphData); - - size = 0; - for (i = 0; i < numEntries; i++) { - newIndexToLoc[i] = size; - - if (i == glyphIndex) { - length = this->PackGlyph(&dst[size],glyphIndex,glyph,this->binSize[asmGLYF],this->binData[asmGLYF],&this->horMetric[glyphIndex]); - } else { - length = oldIndexToLoc[i+1] - oldIndexToLoc[i]; - if (length) { - if (strip < stripHints) - memcpy(&dst[size],&src[oldIndexToLoc[i]],length); - else - length = this->StripGlyphBinary(&dst[size],&src[oldIndexToLoc[i]],length); - } - } - - size += length; - if (size & 1) dst[(size)++] = 0; // zero-pad word align - } - newIndexToLoc[i] = size; - - this->outShortIndexToLocTable = size < 65535*2; - - return size; -} // TrueTypeFont::PackGlyphs - -uint32_t TrueTypeFont::PackGlyph(unsigned char *dst, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfInstruction, sfnt_HorizontalMetrics *hmtx) { - unsigned char *pStart; - uint32_t size; - short i, numberOfPoints, x, y, delta, j, whoseMetrics = 0; - unsigned char bitFlags; - bool composite,useMyMetrics; - - size = 0; - -// if (glyph->numContoursInGlyph == 0) { // fake empty glyph with 1 knot; must be off-curve or else we don't get back the side-bearing points from the apple rasterizer... -// for (i = PHANTOMPOINTS; i > 0; i--) { -// glyph->x[i] = glyph->x[i-1]; -// glyph->y[i] = glyph->y[i-1]; -// glyph->onCurve[i] = glyph->onCurve[i-1]; -// } -// glyph->startPoint[0] = glyph->endPoint[0] = glyph->x[0] = glyph->y[0] = glyph->onCurve[0] = false; -// glyph->numContoursInGlyph++; -// } - - if (glyph->numContoursInGlyph == 0 || (glyph->numContoursInGlyph == 1 && glyph->startPoint[0] == 0 && glyph->endPoint[0] == 0 && glyph->x[0] == 0 && glyph->y[0] == 0 && !glyph->onCurve[0] && glyfBinSize == 0)) { - hmtx->leftSideBearing = 0; - hmtx->advanceWidth = glyph->x[glyph->numContoursInGlyph + 1] - glyph->x[glyph->numContoursInGlyph]; - return 0; // assume this is a dummy point that is neither instructed nor serves as an anchor, hence turn into an empty glyph by leaving with size == 0... - } - - // by now, we have at least one control point - numberOfPoints = glyph->endPoint[glyph->numContoursInGlyph - 1] + 1; // excluding the side bearing points - - pStart = dst; - - composite = useMyMetrics = false; - if ( glyph->componentSize > 0 ) { - int32_t len = 0; - short * flags = nullptr; - - composite = true; - - WRITEALIGNEDWORD( dst, SWAPW(glyph->ComponentVersionNumber) ); - - /* bbox */ - WRITEALIGNEDWORD( dst, SWAPW(glyph->xmin) ); - WRITEALIGNEDWORD( dst, SWAPW(glyph->ymin) ); - WRITEALIGNEDWORD( dst, SWAPW(glyph->xmax) ); - WRITEALIGNEDWORD( dst, SWAPW(glyph->ymax) ); - - i = 0; - do { - flags = &glyph->componentData[i++]; - if (*flags & CSWAPW(USE_MY_METRICS)) { - useMyMetrics = true; - whoseMetrics = glyph->componentData[i++]; - - whoseMetrics = SWAPW(whoseMetrics); - } else - i++; // skip glyphIndex - if (*flags & CSWAPW(ARG_1_AND_2_ARE_WORDS)) i+=2; else i++; - - if (*flags & CSWAPW(WE_HAVE_A_TWO_BY_TWO)) i+=4; - else if (*flags & CSWAPW(WE_HAVE_A_SCALE)) i++; - else if (*flags & CSWAPW(WE_HAVE_AN_X_AND_Y_SCALE)) i+=2; - } while (*flags & CSWAPW(MORE_COMPONENTS)); - - // glyfBinSize may be binary of plain TrueType code or binary of instructions added to composites - // for the purpose of stripping instructions, glyfBinSize is set to 0, after which the flag in the flags - // byte (that is interspersed in the composite code) is inconsistent, hence the above "parsing" and the check below... - if (*flags & CSWAPW(WE_HAVE_INSTRUCTIONS) && glyfBinSize == 0) *flags &= ~CSWAPW(WE_HAVE_INSTRUCTIONS); - - len = glyph->componentSize + glyph->componentSize; - memcpy( (char *) dst,(char *)glyph->componentData, len ); - - dst += len; - if (glyfBinSize > 0) { - - WRITEALIGNEDWORD( dst, (short)SWAPW(glyfBinSize) ); - memcpy((char*)dst,(char*)glyfInstruction,glyfBinSize); // this is already big-endian - dst += glyfBinSize; - } - } else { - short xmin,ymin,xmax,ymax; - - /* bbox and sidebearings */ - xmin = 0x7fff; - ymin = 0x7fff; - xmax = (short)0x8000; - ymax = (short)0x8000; - for ( i = 0; i < numberOfPoints; i++ ) { - xmin = Min(xmin,glyph->x[i]); - xmax = Max(xmax,glyph->x[i]); - ymin = Min(ymin,glyph->y[i]); - ymax = Max(ymax,glyph->y[i]); - } - glyph->xmin = xmin; - glyph->ymin = ymin; - glyph->xmax = xmax; - glyph->ymax = ymax; - - WRITEALIGNEDWORD( dst, SWAPW(glyph->numContoursInGlyph) ); - - WRITEALIGNEDWORD( dst, SWAPW(glyph->xmin) ); - WRITEALIGNEDWORD( dst, SWAPW(glyph->ymin) ); - WRITEALIGNEDWORD( dst, SWAPW(glyph->xmax) ); - WRITEALIGNEDWORD( dst, SWAPW(glyph->ymax) ); - - for ( i = 0; i < glyph->numContoursInGlyph; i++ ) { - WRITEALIGNEDWORD( dst, SWAPW(glyph->endPoint[i]) ); - } - - WRITEALIGNEDWORD( dst, (short)SWAPW(glyfBinSize) ); - memcpy((char*)dst,(char*)glyfInstruction,glyfBinSize); - dst += glyfBinSize; - - /* Calculate flags */ - for ( x = y = i = 0; i < numberOfPoints; i++ ) { - bitFlags = glyph->onCurve[i] & ONCURVE; - delta = glyph->x[i] - x; x = glyph->x[i]; - if ( delta == 0 ) { - bitFlags |= NEXT_X_IS_ZERO; - } else if ( delta >= -255 && delta <= 255 ) { - bitFlags |= XSHORT; - if ( delta > 0 ) - bitFlags |= SHORT_X_IS_POS; - } - - delta = glyph->y[i] - y; y = glyph->y[i]; - if ( delta == 0 ) { - bitFlags |= NEXT_Y_IS_ZERO; - } else if ( delta >= -255 && delta <= 255 ) { - bitFlags |= YSHORT; - if ( delta > 0 ) - bitFlags |= SHORT_Y_IS_POS; - } - this->tmpFlags[i] = bitFlags; - } - - /* Write out bitFlags */ - for ( i = 0; i < numberOfPoints;) { - unsigned char repeat = 0; - for ( j = i+1; j < numberOfPoints && this->tmpFlags[i] == this->tmpFlags[j] && repeat < 255; j++ ) - repeat++; - if ( repeat > 1 ) { - *dst++ = this->tmpFlags[i] | REPEAT_FLAGS; - *dst++ = repeat; - i = j; - } else { - *dst++ = this->tmpFlags[i++]; - } - } - - /* Write out X */ - for ( x = i = 0; i < numberOfPoints; i++ ) { - delta = glyph->x[i] - x; x = glyph->x[i]; - if ( this->tmpFlags[i] & XSHORT ) { - if ( !(this->tmpFlags[i] & SHORT_X_IS_POS) ) delta = -delta; - *dst++ = (unsigned char) delta; - } else if ( !(this->tmpFlags[i] & NEXT_X_IS_ZERO ) ) - WRITENONALIGNEDWORD( dst, delta ); - } - - /* Write out Y */ - for ( y = i = 0; i < numberOfPoints; i++ ) { - delta = glyph->y[i] - y; y = glyph->y[i]; - if ( this->tmpFlags[i] & YSHORT ) { - if ( !(this->tmpFlags[i] & SHORT_Y_IS_POS) ) delta = -delta; - *dst++ = (unsigned char) delta; - } else if ( !(this->tmpFlags[i] & NEXT_Y_IS_ZERO ) ) - WRITENONALIGNEDWORD( dst, delta ); - } - } - - hmtx->leftSideBearing = glyph->xmin - glyph->x[numberOfPoints]; // some rather oldish fonts have x[LSB] != 0 - hmtx->advanceWidth = useMyMetrics ? this->horMetric[whoseMetrics].advanceWidth : glyph->x[numberOfPoints + 1] - glyph->x[numberOfPoints]; - - size = (short)(ptrdiff_t)(dst - pStart); - return size; -} // TrueTypeFont::PackGlyph - -uint32_t TrueTypeFont::StripGlyphBinary(unsigned char *dst, unsigned char *src, uint32_t srcLen) { - unsigned char *glyfPos, *flagPos; - short numContours,i; - unsigned short flags, binLen; - - if (!srcLen) return 0; // nothing else we could squeeze out of it... - - glyfPos = src; - - numContours = COPYALIGNEDWORD(dst,src); - - numContours = SWAPW(numContours); - COPYALIGNEDWORD(dst,src); - COPYALIGNEDWORD(dst,src); - COPYALIGNEDWORD(dst,src); - COPYALIGNEDWORD(dst,src); // skip bounding box - - if (numContours < 0) { - binLen = 0; // assume so far - do { - flagPos = dst; - flags = COPYALIGNEDWORD(dst,src); - - flags = SWAPW(flags); - i = 1; // skip glyphIndex - if (flags & ARG_1_AND_2_ARE_WORDS) i += 2; else i++; - if (flags & WE_HAVE_A_SCALE) i++; - else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) i += 2; - else if (flags & WE_HAVE_A_TWO_BY_TWO) i += 4; - while (i > 0) { - COPYALIGNEDWORD(dst,src); i--; - } - } while (flags & MORE_COMPONENTS); - - if (flags & WE_HAVE_INSTRUCTIONS) { - binLen = READALIGNEDWORD(src); - - binLen = SWAPW(binLen); - flags &= ~WE_HAVE_INSTRUCTIONS; - WRITEALIGNEDWORD(flagPos,SWAPW(flags)); // reset flag, cf. PackGlyph - } - } else { - while (numContours > 0) { COPYALIGNEDWORD(dst,src); numContours--; } // skip end points array - binLen = READALIGNEDWORD(src); - - binLen = SWAPW(binLen); - - WRITEALIGNEDWORD(dst,0); - memcpy((char*)dst,(char*)(src + binLen),glyfPos + srcLen - src - binLen); - } - return srcLen - binLen; -} // TrueTypeFont::StripGlyphBinary - -uint32_t TrueTypeFont::GetPackedGlyphSourceSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, int32_t glitIndex, sfnt_MemDataEntry *memGlit) { - uint32_t size; - - size = 0; - - if (type == 1) { - if (memGlit[glitIndex].glyphCode == PRE_PGM_GLYPH_INDEX) size = prepText->TheLengthInUTF8(); - else if (memGlit[glitIndex].glyphCode == CVT_GLYPH_INDEX) size = cvtText ->TheLengthInUTF8(); - else if (memGlit[glitIndex].glyphCode == HL_GLYPH_INDEX) size = 0; - else if (memGlit[glitIndex].glyphCode == FONT_PGM_GLYPH_INDEX) size = fpgmText->TheLengthInUTF8(); - else if (memGlit[glitIndex].glyphCode == glyphIndex) size = glyfText->TheLengthInUTF8(); - else if (glitIndex < this->maxGlyphs) size = memGlit[glitIndex].length; - } else { // type == 2 - if (memGlit[glitIndex].glyphCode == glyphIndex) size = talkText->TheLengthInUTF8(); - else if (glitIndex < this->maxGlyphs) size = memGlit[glitIndex].length; - } - - if (size & 1) size++; // word align - - return size; -} // TrueTypeFont::GetPackedGlyphSourceSize - -uint32_t TrueTypeFont::GetPackedGlyphSourcesSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, sfnt_MemDataEntry *memGlit) { - uint32_t size; - int32_t glitIndex; - - size = 0; - - for (glitIndex = 0; glitIndex < this->maxGlyphs; glitIndex++) - size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,glyphIndex,glitIndex,memGlit); - - size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,PRE_PGM_GLYPH_INDEX, this->maxGlyphs+1,memGlit); - size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,CVT_GLYPH_INDEX, this->maxGlyphs+2,memGlit); - size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,HL_GLYPH_INDEX, this->maxGlyphs+3,memGlit); - size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,FONT_PGM_GLYPH_INDEX,this->maxGlyphs+4,memGlit); - - return size; -} // TrueTypeFont::GetPackedGlyphSourcesSize - -void TrueTypeFont::PackGlyphSource(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, int32_t glitIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, - uint32_t *dstPos, unsigned char *dst) { - -// B.St.: Notice that PackGlyph, PackGlyphs and probably various such methods do not check against overflowing the sfnt being built in a timely manner, -// i.e. not until it is too late in BuildNewSfnt. This needs some improvements I didn't get around to doing when re-engineering TypeMan/DovMan/TTED. - - if (type == 1) { - if (memGlit[glitIndex].glyphCode == PRE_PGM_GLYPH_INDEX) prepText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); - else if (memGlit[glitIndex].glyphCode == CVT_GLYPH_INDEX) cvtText ->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); - else if (memGlit[glitIndex].glyphCode == HL_GLYPH_INDEX) memGlit[glitIndex].length = 0; - else if (memGlit[glitIndex].glyphCode == FONT_PGM_GLYPH_INDEX) fpgmText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); - else if (memGlit[glitIndex].glyphCode == glyphIndex) glyfText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); - else if (glitIndex < this->maxGlyphs) memcpy((char *)&dst[*dstPos], (char *)(this->GetTablePointer(PRIVATE_PGM1) + memGlit[glitIndex].offset), memGlit[glitIndex].length); // fetch binary - } else { // type == 2 - if (memGlit[glitIndex].glyphCode == glyphIndex) talkText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); - else if (glitIndex < this->maxGlyphs) memcpy((char *)&dst[*dstPos], (char *)(this->GetTablePointer(PRIVATE_PGM2) + memGlit[glitIndex].offset), memGlit[glitIndex].length); - } - fileGlit[glitIndex].offset = SWAPL(*dstPos); - - { - int32_t length = memGlit[glitIndex].length > 0x8000 ? 0x8000 : (unsigned short)memGlit[glitIndex].length; - fileGlit[glitIndex].length = SWAPW(length); - } - *dstPos += memGlit[glitIndex].length; - if (*dstPos & 1) dst[(*dstPos)++] = '\x0D'; // CR-pad (in case length is derived from two consecutive (even!) offsets) word align -} // TrueTypeFont::PackGlyphSource - -void TrueTypeFont::PackGlyphSources(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, - uint32_t *dstLen, unsigned char *dst) { - int32_t glitIndex; - - *dstLen = 0; - for (glitIndex = 0; glitIndex < this->maxGlyphs; glitIndex++) - { - this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,glyphIndex,glitIndex,fileGlit,memGlit,dstLen,dst); - } - this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,PRE_PGM_GLYPH_INDEX, this->maxGlyphs+1,fileGlit,memGlit,dstLen,dst); - this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,CVT_GLYPH_INDEX, this->maxGlyphs+2,fileGlit,memGlit,dstLen,dst); - this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,HL_GLYPH_INDEX, this->maxGlyphs+3,fileGlit,memGlit,dstLen,dst); - this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,FONT_PGM_GLYPH_INDEX,this->maxGlyphs+4,fileGlit,memGlit,dstLen,dst); -} // TrueTypeFont::PackGlyphSources - -/* - * retuns contours and points - */ -bool TrueTypeFont::SubGetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox) { - short i, cont; - unsigned char *sp = 0 <= glyphIndex && glyphIndex < this->numLocaEntries ? (unsigned char *)this->GetTablePointer(tag_GlyphData) + this->IndexToLoc[glyphIndex] : NULL; - short tmp; - unsigned short flags; - sfnt_glyphbbox Sub_bbox; - short NbOfPoints = 0; - - if (sp) { - cont = READALIGNEDWORD( sp ); - - cont = SWAPW(cont); - - bbox->xmin = READALIGNEDWORD( sp ); - - bbox->xmin = SWAPW(bbox->xmin); - bbox->ymin = READALIGNEDWORD( sp ); - - bbox->ymin = SWAPW(bbox->ymin); - bbox->xmax = READALIGNEDWORD( sp ); - - bbox->xmax = SWAPW(bbox->xmax); - bbox->ymax = READALIGNEDWORD( sp ); - - bbox->ymax = SWAPW(bbox->ymax); - - if ( cont < 0 ) { - * ComponentDepth += 1; - if ( * ComponentDepth >= MAXCOMPONENTSIZE ) return false; - do { - - flags = READALIGNEDWORD( sp ); - - flags = SWAPW(flags); - - glyphIndex = READALIGNEDWORD( sp ); - - glyphIndex = SWAPW(glyphIndex); - if (!this->SubGetNumberOfPointsAndContours(glyphIndex,contours, points, ComponentDepth, &Sub_bbox)) return false; - if ( flags & ARG_1_AND_2_ARE_WORDS ) { - /* arg 1 and 2 */ - tmp = READALIGNEDWORD( sp ); - tmp = READALIGNEDWORD( sp ); - } else { - /* arg 1 and 2 as bytes */ - tmp = READALIGNEDWORD( sp ); - } - - if ( flags & WE_HAVE_A_SCALE ) { - /* scale */ - tmp = READALIGNEDWORD( sp ); - } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { - /* xscale, yscale */ - tmp = READALIGNEDWORD( sp ); - tmp = READALIGNEDWORD( sp ); - } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) { - /* xscale, scale01, scale10, yscale */ - tmp = READALIGNEDWORD( sp ); - tmp = READALIGNEDWORD( sp ); - tmp = READALIGNEDWORD( sp ); - tmp = READALIGNEDWORD( sp ); - } - } while ( flags & MORE_COMPONENTS ); - - } else { - *contours += cont; - for ( i = 0; i < cont; i++ ) { -// claudebe the info is an array of last point number - - tmp = READALIGNEDWORD( sp ); - NbOfPoints = SWAPW(tmp) + 1; /* read endpoints */ - } - if ((int32_t)(*points) + (int32_t)NbOfPoints >= MAXPOINTS) return false; - *points += NbOfPoints; - } - } - return true; -} // TrueTypeFont::SubGetNumberOfPointsAndContours - -bool TrueTypeFont::GetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox) { - *contours = 0; - *points = 0; - *ComponentDepth = 0; - /* claudebe call recursively this->SubGetNumberOfPointsAndContours and add the number of contours and points - of all the components and the subcomponents of composite ghyphs */ - return this->SubGetNumberOfPointsAndContours(glyphIndex,contours, points, ComponentDepth, bbox); -} // TrueTypeFont::GetNumberOfPointsAndContours - - - +/// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define _CRT_SECURE_NO_DEPRECATE + +#include // assert +#include +#include +#include "opentypedefs.h" // for SWAP etc. +#include "pch.h" +#include "CvtManager.h" +#include "TTAssembler.h" +#include "TTFont.h" + +#define PRIVATE_GLIT1 'TSI0' /* editor 0 */ +#define PRIVATE_PGM1 'TSI1' /* editor 1 */ +#define PRIVATE_GLIT2 'TSI2' /* editor 2 */ +#define PRIVATE_PGM2 'TSI3' /* editor 3 */ +#define PRIVATE_CMAP 'TSI4' /* Copy of Cmap */ +#define PRIVATE_GROUP 'TSI5' /* group type for each glyph : uppercase, lowercase,... */ + +#define PRIVATE_GSUB 'TSIS' +#define PRIVATE_GPOS 'TSIP' +#define PRIVATE_GDEF 'TSID' +#define PRIVATE_BASE 'TSIB' +#define PRIVATE_JSTF 'TSIJ' + +#define PRIVATE_CVAR 'TSIC' + +#define PRIVATE_STAMP_1 0xABFC1F34 /* fonts stored by char codes... */ +#define PRIVATE_STAMP_2 0xCA0A2117 /* fonts stored by glyph index... */ + +#define GLIT_PAD 5 +#define PRE_PGM_GLYPH_INDEX 0xFFFA +#define CVT_GLYPH_INDEX 0xFFFB +#define HL_GLYPH_INDEX 0xFFFC +#define FONT_PGM_GLYPH_INDEX 0xFFFD +#define PRIVATE_STAMP_INDEX 0xFFFE +static const unsigned short glitPadIndex[GLIT_PAD] = {PRIVATE_STAMP_INDEX, PRE_PGM_GLYPH_INDEX, FONT_PGM_GLYPH_INDEX, CVT_GLYPH_INDEX, HL_GLYPH_INDEX}; + +#define ABSOLUTE_MAX_GLYPHS 0xFFFF // can't have more than 2^16-1 glyphs, unicode 0xffff reserved to indicate "byGlyphIndex..." + +// notice that these macros won't convert big endians into little endians for the windows platform... +#define READALIGNEDWORD( p ) ( p += 2, *(short *)(p-2) ) +#define READNONALIGNEDWORD( p ) ( p += 2, (short)( (((short)*(p-2)) << 8) | *(unsigned char *)(p-1) ) ) +#define COPYALIGNEDWORD(d, s) (s += 2, d += 2, *(short *)(d-2) = *(short *)(s-2)) +#define WRITEALIGNEDWORD( p, x ) ( *((short *)p) = (x), p += 2 ) +#define WRITENONALIGNEDWORD( p, x ) ( *p++ = (unsigned char) ((x) >> 8), *p++ = (unsigned char) (x) ) + +#define DWordPad(x) ((x + 3) & ~3) + +#define OnReserve(x,maxX) ((x)*5 >= (maxX)*4) // reached 80% = 4/5 capacity +#define AddReserve(x) ((x) + (x)/4) + +// character group codes as encoded in a TrueType file; notice that this is not the same encoding as used in the "cvt comment"... +#define TOTALTYPE 8 /* total number of type groups */ +#define CG_UNKNOWN 0 +#define CG_OTHER 1 +#define CG_UPPERCASE 2 +#define CG_LOWERCASE 3 +#define CG_FIGURE 4 +#define CG_PUNCTUATION 5 +#define CG_MS_BOXES 6 +#define CG_DINGBATS 7 + +#define OFFSETIndex 0 +#define SOFFSETIndex 1 +#define ANCHORIndex 2 +#define SANCHORIndex 3 +#define OVERLAPIndex 4 +#define NONOVERLAPIndex 5 +#define USEMYMETRICSIndex 6 +#define SCALEDCOMPONENTOFFSETIndex 7 +#define UNSCALEDCOMPONENTOFFSETIndex 8 + +#define MYOFFSETTABLESIZE 1000 + +// https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats +#define POINTS_ARE_WORDS 0x80 // Flag indicating the data type used for point numbers in this run. If set, the point numbers are stored as unsigned 16-bit values (uint16); if clear, the point numbers are stored as unsigned bytes (uint8). +#define POINT_RUN_COUNT_MASK 0x7F // Mask for the low 7 bits of the control byte to give the number of point number elements, minus 1. + +#define DELTAS_ARE_ZERO 0x80 // Flag indicating that this run contains no data (no explicit delta values are stored), and that the deltas for this run are all zero. +#define DELTAS_ARE_WORDS 0x40 // Flag indicating the data type for delta values in the run. If set, the run contains 16-bit signed deltas (int16); if clear, the run contains 8-bit signed deltas (int8). +#define DELTA_RUN_COUNT_MASK 0x3F // Mask for the low 6 bits to provide the number of delta values in the run, minus one. + +const short charGroupToIntInFile[TOTALTYPE] = {CG_UNKNOWN, CG_OTHER, CG_UPPERCASE, CG_LOWERCASE, CG_FIGURE, CG_UNKNOWN, CG_UNKNOWN, CG_UNKNOWN}; + +const CharGroup intInFileToCharGroup[TOTALTYPE] = {anyGroup, otherCase, upperCase, lowerCase, figureCase, otherCase, otherCase, otherCase}; // map CG_MS_BOXES to CG_OTHER as per elik + +typedef struct { + unsigned char bCharSet; // 0 = no subset; 1 = win ansi + unsigned char xRatio; + unsigned char yStartRatio; + unsigned char yEndRatio; +} VDMXRatio; + +typedef struct { + unsigned short ppemHeight; + short yMax; + short yMin; +} VDMXEntry; + +typedef struct { + unsigned short numEntries; + unsigned char startPpemHeight; + unsigned char endPpemHeight; + VDMXEntry entry[1]; +} VDMXGroup; + +typedef struct { + unsigned short version; + unsigned short numGroups; + unsigned short numRatios; + VDMXRatio ratio[1]; +} VDMXTable; + +typedef struct { + unsigned int glyphId; + const char * glyphName; +} PSNAMELIST; + +TrueTypeGlyph::TrueTypeGlyph(void) { +} // TrueTypeGlyph::TrueTypeGlyph + +TrueTypeGlyph::~TrueTypeGlyph(void) { + // nix +} // TrueTypeGlyph::~TrueTypeGlyph + +int32_t ColorTransition(Vector V[], Vector W[]); +int32_t ColorTransition(Vector V[], Vector W[]) { // cf. also Intersect in "MathUtils.h" + int32_t a11,a12,a21,a22,b1,b2,det,t1,t2; + + b1 = V[0].x - W[1].x; a11 = V[0].x - V[1].x; a12 = W[2].x - W[1].x; + b2 = V[0].y - W[1].y; a21 = V[0].y - V[1].y; a22 = W[2].y - W[1].y; + det = a11*a22 - a12*a21; + if (det == 0) return 0; // parallel + t1 = a22*b1 - a12*b2; + t2 = a11*b2 - a21*b1; + return (Sgn(t1) == Sgn(det) && 0 < Abs(t1) && Abs(t1) < Abs(det) && // intersection point is inside line V + ((Sgn(t2) == Sgn(det) && 0 < Abs(t2) && Abs(t2) < Abs(det)) || // intersection point is inside line W + (t2 == det && Sgn(VectProdP(V[0],W[1],V[0],W[2]))*Sgn(VectProdP(V[0],W[3],V[0],W[2])) < 0))) ? 1 : 0; // or at the end of W and W arrives/departs on different sides of V +} // ColorTransition + +LinkColor TrueTypeGlyph::TheColor(short from, short to) { + Vector V[3],D[2],C[2],W[4]; + int32_t cont,pred,knot,succ,start,zero,end,n,parity,sgn,dir[4]; + + for (cont = 0; cont < this->numContoursInGlyph; cont++) { + start = this->startPoint[cont]; + n = this->endPoint[cont] - start + 1; + V[0].x = this->x[start+n-1]; V[1].x = this->x[start]; + V[0].y = this->y[start+n-1]; V[1].y = this->y[start]; + D[0] = SubV(V[1],V[0]); + for (knot = 0; knot < n; knot++) { + V[2].x = this->x[start + (knot + 1)%n]; + V[2].y = this->y[start + (knot + 1)%n]; + D[1] = SubV(V[2],V[1]); + sgn = D[0].x*D[1].y - D[0].y*D[1].x; + this->dirChange[start + knot] = (short)Sgn(sgn); // +1 => left turn, -1 => right turn, 0 => straight + V[0] = V[1]; V[1] = V[2]; D[0] = D[1]; + } + } + + V[0].x = this->x[from] << 1; + V[0].y = this->y[from] << 1; + V[1].x = this->x[to] << 1; + V[1].y = this->y[to] << 1; + C[0] = ShrV(AddV(V[0],V[1]),1); // use extra bit at end to avoid precision loss upon dividing + C[1].x = -32768; C[1].y = C[0].y-1; // and make sure color transition test line does not align with any straight line in the glyph... + parity = 0; + for (cont = 0; cont < this->numContoursInGlyph; cont++) { + start = this->startPoint[cont]; + end = this->endPoint[cont]; + n = end - start + 1; + for (pred = start + n - 1; pred >= start && !(dir[0] = this->dirChange[pred]); pred--); // treat aligned straight lines as one single straight line + for (zero = start; zero < start + n && !(dir[1] = this->dirChange[zero]); zero++); // treat aligned straight lines as one single straight line + if (zero < start + n) { // else pathological case of flat polygon + W[0].x = this->x[pred] << 1; + W[0].y = this->y[pred] << 1; + W[1].x = this->x[zero] << 1; + W[1].y = this->y[zero] << 1; + knot = zero; + do { + do knot = knot == end ? start : knot + 1; while (!(dir[2] = this->dirChange[knot])); + W[2].x = this->x[knot] << 1; + W[2].y = this->y[knot] << 1; + succ = knot; + do succ = succ == end ? start : succ + 1; while (!(dir[3] = this->dirChange[succ])); + W[3].x = this->x[succ] << 1; + W[3].y = this->y[succ] << 1; + if (Collinear(W[1],V[0],W[2],notOutside) && Collinear(W[1],V[1],W[2],notOutside)) + return dir[1] + dir[2] == 2 ? linkWhite : linkBlack; // V completely contained in W, concave edges are white, convex ones and stairs are black + else if (VectProdP(V[0],V[1],W[1],W[2]) == 0 && + (Collinear(W[1],V[0],W[2],inside) || Collinear(W[1],V[1],W[2],inside) || Collinear(V[0],W[1],V[1],inside) || Collinear(V[0],W[2],V[1],inside))) + return linkGrey; // V partly overlaps W or v.v. + else if (ColorTransition(V,W)) + return linkGrey; + parity += ColorTransition(C,W); // else we are still either all black or all white, so keep track of parity + W[0] = W[1]; W[1] = W[2]; dir[0] = dir[1]; dir[1] = dir[2]; + } while (knot != zero); + } + } + return parity & 1 ? linkBlack : linkWhite; +} // TrueTypeGlyph::TheColor + +TrueTypeFont::TrueTypeFont(void) { + int32_t i; + + this->sfntHandle = NULL; + this->sfntTmpHandle = NULL; + this->offsetTable = NULL; + this->tmpOffsetTable = NULL; + this->incrBuildSfntData = NULL; + this->cvt = NULL; + this->IndexToLoc = NULL; + this->tmpIndexToLoc = NULL; + this->glit1 = NULL; + this->glit2 = NULL; + this->charCodeOf = NULL; + this->charGroupOf = NULL; + this->glyphIndexMap = NULL; + for (i = 0; i < Len(this->binData); i++) { + this->binSize[i] = 0; + this->binData[i] = NULL; + } + this->devMetricsPtr = NULL; + this->ltshPtr = NULL; + this->vdmxPtr = NULL; + this->horMetric = NULL; + this->postScriptNames = NULL; + + this->bVariationTypeface_ = false; + this->axisCount_ = 0; +} // TrueTypeFont::TrueTypeFont + +bool TrueTypeFont::Create() { +// create this part of font only once to avoid memory fragmentation + this->maxSfntSize = trialSfntSize; + this->maxTmpSfntSize = trialSfntSize; + this->maxGlitEntries = trialNumGlyphs + 5; +# + this->sfntHandle = (unsigned char *)NewP(this->maxSfntSize); + this->sfntTmpHandle = (unsigned char *)NewP(this->maxSfntSize); + + this->offsetTable = (sfnt_OffsetTable *) NewP(MYOFFSETTABLESIZE); + this->tmpOffsetTable = (sfnt_OffsetTable *) NewP(MYOFFSETTABLESIZE); +// this->incrBuildSfntData allocated and deallocated locally + this->cvt = NewControlValueTable(); + this->IndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*this->maxGlitEntries); + this->tmpIndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*this->maxGlitEntries); + this->glit1 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*this->maxGlitEntries); + this->glit2 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*this->maxGlitEntries); + this->charCodeOf = (uint32_t *)NewP(sizeof(uint32_t)*this->maxGlitEntries); + this->charGroupOf = (unsigned char *)NewP(this->maxGlitEntries); + this->glyphIndexMap = new std::vector; + this->tmpFlags = (unsigned char *)NewP(MAXPOINTS); + this->hdmxBinSize = 0; + this->ltshBinSize = 0; + this->vdmxBinSize = 0; + this->gaspBinSize = 0; + this->tsicBinSize = 0; + this->cvarBinSize = 0; + this->horMetric = (sfnt_HorizontalMetrics *)NewP(sizeof(sfnt_HorizontalMetrics)*this->maxGlitEntries); + this->gaspLoaded = false; + + return this->sfntHandle && this->sfntTmpHandle && this->offsetTable && this->tmpOffsetTable && this->cvt && this->IndexToLoc && this->tmpIndexToLoc && + this->glit1 && this->glit2 && this->charCodeOf && this->charGroupOf && this->horMetric && this->glyphIndexMap; +} // TrueTypeFont::Create + +TrueTypeFont::~TrueTypeFont(void) { + int32_t i; + + if (this->horMetric) DisposeP((void **)&this->horMetric); + if (this->vdmxPtr) DisposeP((void **)&this->vdmxPtr); + if (this->ltshPtr) DisposeP((void **)&this->ltshPtr); + if (this->devMetricsPtr) DisposeP((void **)&this->devMetricsPtr); + if (this->tmpFlags) DisposeP((void **)&this->tmpFlags); + for (i = 0; i < Len(this->binData); i++) { + this->binSize[i] = 0; + if (this->binData[i] != NULL) DisposeP((void **)&this->binData[i]); + } + if (this->glyphIndexMap) { + delete this->glyphIndexMap; + this->glyphIndexMap = NULL; + } + if (this->charGroupOf) DisposeP((void **)&this->charGroupOf); + if (this->charCodeOf) DisposeP((void **)&this->charCodeOf); + if (this->glit2) DisposeP((void **)&this->glit2); + if (this->glit1) DisposeP((void **)&this->glit1); + if (this->tmpIndexToLoc) DisposeP((void **)&this->tmpIndexToLoc); + if (this->IndexToLoc) DisposeP((void **)&this->IndexToLoc); + if (this->cvt) delete this->cvt; +// this->incrBuildSfntData allocated and deallocated locally + if (this->tmpOffsetTable) DisposeP((void **)&this->tmpOffsetTable); + if (this->offsetTable) DisposeP((void **)&this->offsetTable); + + if (this->postScriptNames) { + delete this->postScriptNames; + this->postScriptNames = NULL; + } + + if (this->sfntTmpHandle) DisposeP((void **)&this->sfntTmpHandle); + if (this->sfntHandle) DisposeP((void **)&this->sfntHandle); + +} // TrueTypeFont::~TrueTypeFont + +void TrueTypeFont::AssertMaxSfntSize(uint32_t minSfntSize, bool assertMainHandle, bool assertTempHandle) { + bool enough; + + minSfntSize = AddReserve(((minSfntSize + 0xfffff) >> 20) << 20); // round up to the nearest MB and add reserve + + enough = true; // assume + if (assertMainHandle && minSfntSize > this->maxSfntSize) enough = false; + if (assertTempHandle && minSfntSize > this->maxTmpSfntSize) enough = false; + if (enough) return; // we're done, enough reserve + + if (assertTempHandle && this->sfntTmpHandle) DisposeP((void **)&this->sfntTmpHandle); + if (assertMainHandle && this->sfntHandle) DisposeP((void **)&this->sfntHandle); + + if (assertMainHandle) { + this->sfntHandle = (unsigned char *)NewP(minSfntSize); +# + if (this->sfntHandle) { + this->maxSfntSize = minSfntSize; + } else { + this->maxSfntSize = 0; // we've just de-allocated it and failed to allocate it + } + } + if (assertTempHandle) { + this->sfntTmpHandle = (unsigned char *)NewP(minSfntSize); + + if (this->sfntTmpHandle) { + this->maxTmpSfntSize = minSfntSize; + } else { + this->maxTmpSfntSize = 0; // we've just de-allocated it and failed to allocate it + } + } +} // TrueTypeFont::AssertMaxSfntSize + +void TrueTypeFont::AssertMaxGlyphs(int32_t minGlyphs) { + + if (AddReserve(minGlyphs) < this->maxGlitEntries) return; // we're done, enough reserve + + if (this->horMetric) DisposeP((void**)&this->horMetric); + if (this->charGroupOf) DisposeP((void**)&this->charGroupOf); + if (this->charCodeOf) DisposeP((void**)&this->charCodeOf); + if (this->glit2) DisposeP((void**)&this->glit2); + if (this->glit1) DisposeP((void**)&this->glit1); + if (this->tmpIndexToLoc) DisposeP((void**)&this->tmpIndexToLoc); + if (this->IndexToLoc) DisposeP((void**)&this->IndexToLoc); + + minGlyphs = AddReserve(((minGlyphs + 0x3ff) >> 10) << 10); // round up to the nearest k and add reserve + minGlyphs = Min(minGlyphs,ABSOLUTE_MAX_GLYPHS); // but don't push the limit + + this->IndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*minGlyphs); + this->tmpIndexToLoc = (uint32_t *)NewP(sizeof(int32_t)*minGlyphs); + this->glit1 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*minGlyphs); + this->glit2 = (sfnt_MemDataEntry *)NewP(sizeof(sfnt_MemDataEntry)*minGlyphs); + this->charCodeOf = (uint32_t *)NewP(sizeof(uint32_t)*minGlyphs); + this->charGroupOf = (unsigned char *)NewP(minGlyphs); + this->horMetric = (sfnt_HorizontalMetrics *)NewP(sizeof(sfnt_HorizontalMetrics)*minGlyphs); + + this->maxGlitEntries = this->IndexToLoc && this->tmpIndexToLoc && this->glit1 && this->glit2 && this->charCodeOf && this->charGroupOf && this->horMetric ? minGlyphs : 0; +} // TrueTypeFont::AssertMaxGlyphs + +void MaxSfntSizeError(const wchar_t from[], int32_t size, wchar_t errMsg[], size_t errMsgLen); +void MaxSfntSizeError(const wchar_t from[], int32_t size, wchar_t errMsg[], size_t errMsgLen) { + swprintf(errMsg,errMsgLen,WIDE_STR_FORMAT L", \r" BULLET L" Unable to allocate %li to work on this font.",from, size); +} // MaxSfntSizeError + +bool TrueTypeFont::SetSfnt(short platformID, short encodingID, wchar_t errMsg[], size_t errMsgLen) +{ + this->UnpackFvar(); + this->UnpackAvar(); + + cvtVariationInterpolator_ = std::make_shared(); + + if (instanceManager_ == nullptr) + { + instanceManager_ = std::make_shared(); + } + + variationAxisTags_ = std::make_shared>(); + + if (this->HasFvar()) + { + axisCount_ = fvar_.axisCount; + bVariationTypeface_ = true; + + // Set name table names per axis + for (size_t index = 0; index < axisCount_; index++) + { + uint32_t tag = fvar_.axisRecords[index].axisTag; + variationAxisTags_->insert(variationAxisTags_->end(), tag); + } + + instanceManager_->SetAxisCount(axisCount_); + + if (this->HasPrivateCvar()) + { + TSICHeader tsic; + this->tsicError = !this->GetPrivateCvar(tsic); + if (this->tsicError) + { + std::wstring str = L"Error: Unable to load TSIC table!" BRK; + return false; + } + else + { + this->MergePrivateCvarWithInstanceManager(tsic); + } + } + + instanceManager_->Sort(); + } + + return true; +} // SetSfnt + + void TrueTypeFont::UnpackFvar(void) +{ + unsigned char* charStart = this->GetTablePointer(tag_FontVariations); + unsigned short* start = reinterpret_cast(charStart); + if (start == nullptr) + return; + + fvar_.axisRecords.clear(); + fvar_.instanceRecords.clear(); + + unsigned short* packed = start; + + fvar_.majorVersion = SWAPWINC(packed); + fvar_.minorVersion = SWAPWINC(packed); + fvar_.offsetToAxesArray = SWAPWINC(packed); + fvar_.countSizePairs = SWAPWINC(packed); + fvar_.axisCount = SWAPWINC(packed); + fvar_.axisSize = SWAPWINC(packed); + fvar_.instanceCount = SWAPWINC(packed); + fvar_.instanceSize = SWAPWINC(packed); + + packed = (unsigned short*)(charStart + fvar_.offsetToAxesArray); + for (int16_t axisIndex = 0; axisIndex < fvar_.axisCount; axisIndex++) + { + VariationAxisRecord axisRecord; + unsigned short* packed1 = packed; + + uint32_t* ptag = reinterpret_cast(packed1); + axisRecord.axisTag = *ptag; + packed1 += 2; + + int32_t* pminValue = reinterpret_cast(packed1); + int32_t minValue = SWAPL(*pminValue); + axisRecord.minValue.SetRawValue(minValue); + packed1 += 2; + + // int32_t* pdefaultValue = reinterpret_cast(packed1); + int32_t defaultValue = SWAPL(*pminValue); + axisRecord.defaultValue.SetRawValue(defaultValue); + packed1 += 2; + + // int32_t* pmaxValue = reinterpret_cast(packed1); + int32_t maxValue = SWAPL(*pminValue); + axisRecord.maxValue.SetRawValue(maxValue); + packed1 += 2; + + axisRecord.flags = SWAPWINC(packed1); + axisRecord.nameID = SWAPWINC(packed1); + + fvar_.axisRecords.push_back(axisRecord); + + packed = reinterpret_cast(reinterpret_cast(packed) + fvar_.axisSize); + } + + for (uint16_t instanceIndex = 0; instanceIndex < fvar_.instanceCount; instanceIndex++) + { + VariationInstanceRecord instanceRecord; + unsigned short* packed1 = packed; + + instanceRecord.nameID = SWAPWINC(packed1); + instanceRecord.flags = SWAPWINC(packed1); + + for (int16_t axisIndex = 0; axisIndex < fvar_.axisCount; axisIndex++) + { + int32_t* pcoord = reinterpret_cast(packed1); + Fixed16_16 coord(SWAPL(*pcoord)); + packed1 += 2; + + instanceRecord.coordinates.push_back(coord); + } + + fvar_.instanceRecords.push_back(instanceRecord); + + // don't bother with optional postScriptNameID for now + + packed = reinterpret_cast(reinterpret_cast(packed) + fvar_.instanceSize); + } +} + +void TrueTypeFont::UnpackAvar(void) +{ + unsigned short* packed = reinterpret_cast(this->GetTablePointer(tag_AxisVariations)); + if (packed == nullptr) + return; + + avar_.axisSegmentMaps.clear(); + + avar_.majorVersion = SWAPWINC(packed); + avar_.minorVersion = SWAPWINC(packed); + avar_.reserved = SWAPWINC(packed); + avar_.axisCount = SWAPWINC(packed); + + // segment maps array + for (uint16_t axisIndex = 0; axisIndex < avar_.axisCount; ++axisIndex) + { + ShortFracSegment segment; + segment.pairCount = SWAPWINC(packed); + for (uint16_t pairIndex = 0; pairIndex < segment.pairCount; pairIndex++) + { + int16_t fromCoord = SWAPWINC(packed); + int16_t toCoord = SWAPWINC(packed); + Fixed2_14 from(fromCoord); + Fixed2_14 to(toCoord); + ShortFracCorrespondence map{from ,to}; + segment.axisValueMaps.push_back(map); + } + avar_.axisSegmentMaps.push_back(segment); + } +} + +bool TrueTypeFont::IsMakeTupleName(const std::wstring &name) const +{ + std::wstring opt1 = L"User"; + std::wstring opt2 = L"Unnamed"; + + if (name.size() >= opt1.size() && name.compare(0, opt1.size(), opt1) == 0) + return true; + + if (name.size() >= opt2.size() && name.compare(0, opt2.size(), opt2) == 0) + return true; + + return false; +} // TrueTypeFont::IsMakeTupleName + +bool TrueTypeFont::Read(File *file, TrueTypeGlyph *glyph, short *platformID, short *encodingID, wchar_t errMsg[], size_t errMsgLen) { + int32_t glyphIndex; + + this->sfntSize = file->Length(); + + this->AssertMaxSfntSize(this->sfntSize,true,true); + + if (this->sfntSize > this->maxSfntSize) { + MaxSfntSizeError(L"Read: This font is too large",this->sfntSize,errMsg,errMsgLen); return false; + } + file->ReadBytes(this->sfntSize, this->sfntHandle); + if (file->Error()) { + swprintf(errMsg, errMsgLen, L"Read: I/O error reading this font"); return false; + } + + if (!this->UnpackHeadHheaMaxpHmtx(errMsg, errMsgLen)) return false; + + if(*platformID == 3 && *encodingID == 1) + *encodingID = 10; // lets first try 3,10 and default lower if not present + + if (!this->CMapExists(*platformID,*encodingID) && !this->DefaultCMap(platformID,encodingID,errMsg, errMsgLen)) return false; + + if (!(this->UnpackGlitsLoca(errMsg, errMsgLen) && this->UpdateMaxPointsAndContours(errMsg, errMsgLen) && this->UnpackCMap(*platformID, *encodingID, errMsg, errMsgLen) && this->UnpackCharGroup(errMsg, errMsgLen))) + return false; + + // Clear for new font. + if (instanceManager_ != nullptr) + { + instanceManager_->clear(); + } + + if (!this->SetSfnt(*platformID, *encodingID, errMsg, errMsgLen)) + return false; + + // not the smartest way to get these numbers, another historical legacy + if ((glyphIndex = this->GlyphIndexOf(L'H')) == INVALID_GLYPH_INDEX) this->capHeight = this->unitsPerEm; + else if (this->GetGlyph(glyphIndex, glyph, errMsg, errMsgLen)) + this->capHeight = glyph->ymax; + else return false; + if ((glyphIndex = this->GlyphIndexOf(L'x')) == INVALID_GLYPH_INDEX) this->xHeight = this->unitsPerEm; + else if (this->GetGlyph(glyphIndex, glyph, errMsg, errMsgLen)) + this->xHeight = glyph->ymax; + else return false; + if ((glyphIndex = this->GlyphIndexOf(L'p')) == INVALID_GLYPH_INDEX) this->descenderHeight = 0; + else if (this->GetGlyph(glyphIndex, glyph, errMsg, errMsgLen)) + this->descenderHeight = glyph->ymin; + else return false; + + // Clear for new font. + if (this->postScriptNames) { + delete this->postScriptNames; + this->postScriptNames = NULL; + } + + return true; // by now +} // TrueTypeFont::Read + +bool TrueTypeFont::Write(File *file, wchar_t errMsg[], size_t errMsgLen) { + file->WriteBytes(this->sfntSize, this->sfntHandle); + //if (!file->Error()) file->SetPos(this->sfntSize,true); // truncate file to current size + if (file->Error()) { + swprintf(errMsg, errMsgLen, L"I/O error writing this font"); + return false; + } + return true; // by now +} // TrueTypeFont::Write + +ControlValueTable *TrueTypeFont::TheCvt(void) { + return this->cvt; +} // TrueTypeFont::TheCvt + +bool TrueTypeFont::GetCvt(TextBuffer *cvtText, wchar_t errMsg[], size_t errMsgLen) { + int32_t size,entries,i; + short *cvt; + wchar_t buffer[maxLineSize]; + + cvt = (short*)this->GetTablePointer(tag_ControlValue); + size = this->GetTableLength(tag_ControlValue); + entries = size >> 1; + if (entries < 0 || MAXCVT < entries) { swprintf(errMsg, errMsgLen, L"GetCvt: There are too many cvt entries %li",entries); return false; } + + if (!this->GetSource(true,CVT_GLYPH_INDEX,cvtText,errMsg, errMsgLen)) return false; + + if (cvtText->TheLength() == 0) { // decode binary cvt + for (i = 0; i < entries; i++) { swprintf(buffer, errMsgLen, L"%i: %hi\r",i,SWAPW(cvt[i])); cvtText->Append(buffer); } + + } + this->cvt->PutCvtBinary(size,(unsigned char *)cvt); + return true; // by now +} // TrueTypeFont::GetCvt + +bool TrueTypeFont::GetPrep(TextBuffer *prepText, wchar_t errMsg[], size_t errMsgLen) { + unsigned char *data; + int32_t size; + + errMsg[0] = L'\0'; + data = this->GetTablePointer(tag_PreProgram); + size = this->GetTableLength(tag_PreProgram); + if (size > MAXBINSIZE) { + swprintf(errMsg,errMsgLen,L"GetPrep: pre-program is %li bytes long (cannot be longer than %li bytes)",size,MAXBINSIZE); + return false; + } + return this->UpdateBinData(asmPREP,size,data) && this->GetSource(true,PRE_PGM_GLYPH_INDEX,prepText,errMsg, errMsgLen); // get source after binary, in case binary exists but source doesn't... +} // TrueTypeFont::GetPrep + +int32_t TrueTypeFont::PrepBinSize(void) { + return this->binSize[asmPREP]; +} // TrueTypeFont::PrepBinSize + +bool TrueTypeFont::GetFpgm(TextBuffer *fpgmText, wchar_t errMsg[], size_t errMsgLen) { + unsigned char *data; + int32_t size; + + errMsg[0] = L'\0'; + data = this->GetTablePointer(tag_FontProgram); + size = this->GetTableLength(tag_FontProgram); + if (size > MAXBINSIZE) { + swprintf(errMsg,errMsgLen, L"GetFpgm: font program is %li bytes long (cannot be longer than %li bytes)",size,MAXBINSIZE); + return false; + } + return this->UpdateBinData(asmFPGM,size,data) && this->GetSource(true,FONT_PGM_GLYPH_INDEX,fpgmText,errMsg, errMsgLen); // get source after binary, in case binary exists but source doesn't... +} // TrueTypeFont::GetFpgm + +int32_t TrueTypeFont::FpgmBinSize(void) { + return this->binSize[asmFPGM]; +} // TrueTypeFont::FpgmBinSize + +bool TrueTypeFont::GetGlyf(int32_t glyphIndex, TextBuffer *glyfText, wchar_t errMsg[], size_t errMsgLen) { + return this->GetSource(true, glyphIndex, glyfText, errMsg, errMsgLen); + // here we don't get any binary, this is done in GetGlyph, which also deals with the glyph's bounding box or composite information +} // TrueTypeFont::GetGlyf + + +bool TrueTypeFont::GetTalk(int32_t glyphIndex, TextBuffer *talkText, wchar_t errMsg[], size_t errMsgLen) { + return this->GetSource(false, glyphIndex, talkText, errMsg, errMsgLen); +} // TrueTypeFont::GetTalk + + +bool TrueTypeFont::GetGlyph(int32_t glyphIndex, TrueTypeGlyph *glyph, wchar_t errMsg[], size_t errMsgLen) { + short i,end = 0; + bool emptyGlyph,weHaveInstructions; + unsigned char *sp; + char signedByte; + short numContoursInGlyph; + unsigned short flags,cgIdx; + short signedWord; + unsigned short unsignedWord; + int32_t numKnots,numContours,componentDepth,binSize; + TrueTypeComponent *component; + int32_t lsb = 0, advWidth = 0; + + if (glyphIndex < 0 || this->numLocaEntries <= glyphIndex) { + swprintf(errMsg, errMsgLen, L"TrueTypeFont::GetGlyph: glyphIndex %li is out of range",glyphIndex); return false; + } + + GetHMTXEntry(glyphIndex, &lsb, &advWidth); + + sp = (unsigned char *)this->GetTablePointer(tag_GlyphData) + this->IndexToLoc[glyphIndex]; + emptyGlyph = this->IndexToLoc[glyphIndex] == this->IndexToLoc[glyphIndex+1]; + + glyph->numContoursInGlyph = 0; + glyph->composite = glyph->useMyMetrics = false; + this->UpdateBinData(asmGLYF,0,NULL); + glyph->componentSize = 0; + + if (emptyGlyph) { + F26Dot6 fxXMinMinusLSB = 0 - lsb; + + glyph->xx[LEFTSIDEBEARING] = fxXMinMinusLSB; + glyph->xx[RIGHTSIDEBEARING] = fxXMinMinusLSB + advWidth; + glyph->yy[LEFTSIDEBEARING] = 0; + glyph->yy[RIGHTSIDEBEARING] = 0; + glyph->onCurve[PHANTOMPOINTS] = glyph->onCurve[PHANTOMPOINTS-1] = true; + glyph->onCurve[0] = false; + + glyph->startPoint[0] = glyph->endPoint[0] = 0; + glyph->numContoursInGlyph = 0; + + for (i = 0; i <= end + PHANTOMPOINTS; i++) + { // lsb, rsb + glyph->x[i] = (short)(glyph->xx[i]); // >> places6); + glyph->y[i] = (short)(glyph->yy[i]); // >> places6); + } + } + + if (emptyGlyph) return true; // nothing else we could unpack + + signedWord = READALIGNEDWORD( sp ); + numContoursInGlyph = SWAPW(signedWord); + + if (numContoursInGlyph > MAXCONTOURS) { + swprintf(errMsg, errMsgLen, L"GetGlyph: glyph %li has %hi contours (cannot have more than %hi)",glyphIndex,numContoursInGlyph,MAXCONTOURS); return false; + } + + glyph->startPoint[0] = 0; + glyph->endPoint[0] = 0; + glyph->onCurve[0] = true; + glyph->x[0] = 0; + glyph->y[0] = 0; + glyph->xx[0] = 0; + glyph->yy[0] = 0; + + glyph->numContoursInGlyph = 0; + signedWord = READALIGNEDWORD(sp); + glyph->xmin = SWAPW(signedWord); + + signedWord = READALIGNEDWORD(sp); + glyph->ymin = SWAPW(signedWord); + + signedWord = READALIGNEDWORD(sp); + glyph->xmax = SWAPW(signedWord); + + signedWord = READALIGNEDWORD(sp); + glyph->ymax = SWAPW(signedWord); + + glyph->bluePrint.numComponents = 0; + glyph->bluePrint.useMyMetrics = INVALID_GLYPH_INDEX; // use nobody's metrics so far + + if ( numContoursInGlyph < 0 ) { + glyph->composite = true; + weHaveInstructions = false; + int32_t accumulatedKnots = 0; + int32_t accumulatedContours = 0; + do { + if (glyph->bluePrint.numComponents >= MAXNUMCOMPONENTS) { + swprintf(errMsg, errMsgLen, L"GetGlyph: glyph %hu has more than %hu components ",glyphIndex,(unsigned short)MAXNUMCOMPONENTS); return false; + } + + component = &glyph->bluePrint.component[glyph->bluePrint.numComponents]; + + flags = *(unsigned short *)(sp+0); flags = SWAPW(flags); + cgIdx = *(unsigned short *)(sp+2); cgIdx = SWAPW(cgIdx); + + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + + weHaveInstructions = (flags & WE_HAVE_INSTRUCTIONS) != 0; // otherwise VC++ 5.0 does the bitwise AND in 16 bit and then moves a BYTE, dropping the WE_HAVE_INSTRUCTIONS (0x0100) bit + + component->glyphIndex = cgIdx; + component->unicode = this->charCodeOf[cgIdx]; + component->rounded = (flags & ROUND_XY_TO_GRID) != 0; + component->anchor = (flags & ARGS_ARE_XY_VALUES) == 0; + component->offsetX = component->offsetY = 0; + component->parent = component->child = 0; + component->transform[0][0] = component->transform[1][1] = 16384; // 1 in 2.14 format + component->transform[0][1] = component->transform[1][0] = 0; + + numKnots = numContours = componentDepth = 0; + if (GetNumPointsAndContours(cgIdx,&numKnots,&numContours,&componentDepth)) { + accumulatedKnots += numKnots; + accumulatedContours += numContours; + if (accumulatedContours > 0 && accumulatedKnots > 0) { + glyph->endPoint[accumulatedContours - 1] = accumulatedKnots - 1; + } + glyph->numContoursInGlyph += numContours; + component->numContours = (unsigned short)numContours; + } else { + swprintf(errMsg, errMsgLen, L"GetGlyph: failed to obtain number of contours for component %i of glyph %i",(int32_t)cgIdx,glyphIndex); return false; + } + + if (flags & USE_MY_METRICS) { + glyph->useMyMetrics = true; + if (glyph->bluePrint.useMyMetrics == INVALID_GLYPH_INDEX) glyph->bluePrint.useMyMetrics = cgIdx; // first one wins + } + if ( flags & ARG_1_AND_2_ARE_WORDS ) { + /* arg 1 and 2 */ + signedWord = *(signed short *)(sp+0); component->offsetX = SWAPW(signedWord); + signedWord = *(signed short *)(sp+2); component->offsetY = SWAPW(signedWord); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + } else { + /* arg 1 and 2 as bytes */ + signedByte = *(signed char *)(sp+0); component->offsetX = signedByte; + signedByte = *(signed char *)(sp+1); component->offsetY = signedByte; + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + } + + if (component->anchor) { + component->parent = component->offsetX; component->offsetX = 0; + component->child = component->offsetY; component->offsetY = 0; + } + + if ( flags & WE_HAVE_A_SCALE ) { + /* scale */ + signedWord = *(signed short *)(sp+0); component->transform[0][0] = component->transform[1][1] = SWAPW(signedWord); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { + /* xscale, yscale */ + signedWord = *(signed short *)(sp+0); component->transform[0][0] = SWAPW(signedWord); + signedWord = *(signed short *)(sp+2); component->transform[1][1] = SWAPW(signedWord); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) { + /* xscale, scale01, scale10, yscale */ + signedWord = *(signed short *)(sp+0); component->transform[0][0] = SWAPW(signedWord); + signedWord = *(signed short *)(sp+2); component->transform[0][1] = SWAPW(signedWord); + signedWord = *(signed short *)(sp+4); component->transform[1][0] = SWAPW(signedWord); + signedWord = *(signed short *)(sp+6); component->transform[1][1] = SWAPW(signedWord); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + glyph->componentData[ glyph->componentSize++] = READALIGNEDWORD( sp ); + } + component->identity = component->transform[0][0] == 16384 && component->transform[1][1] == 16384 // 1 in 2.14 format + && component->transform[0][1] == 0 && component->transform[1][0] == 0; + + if ( glyph->componentSize > MAXCOMPONENTSIZE ) { // maybe too late already... + swprintf(errMsg, errMsgLen, L"GetGlyph: glyph %li has %hi bytes of component data (cannot have more than %hi)",glyphIndex,glyph->componentSize,MAXCOMPONENTSIZE); return false; + } + + glyph->bluePrint.numComponents++; + + } while ( flags & MORE_COMPONENTS ); + glyph->ComponentVersionNumber = numContoursInGlyph; + + if (weHaveInstructions) { + binSize = READALIGNEDWORD(sp); + + binSize = SWAPW(binSize); + if (binSize > MAXBINSIZE) { + swprintf(errMsg, errMsgLen, L"GetGlyph: glyph %li has %li bytes of instruction (cannot have more than %li)", glyphIndex, binSize, MAXBINSIZE); return false; + } + + this->UpdateBinData(asmGLYF, binSize, sp); + sp += binSize; + } + + } else { + + glyph->numContoursInGlyph = numContoursInGlyph; + + uint8 abyOnCurve[MAXPOINTS]; + numKnots = 0; + + if (numContoursInGlyph > 0) + { + unsignedWord = READALIGNEDWORD(sp); + glyph->endPoint[0] = SWAPW(unsignedWord); + + numKnots = glyph->endPoint[0] + 1; + + for (int32 lContourIndex = 1; lContourIndex < numContoursInGlyph; lContourIndex++) + { + glyph->startPoint[lContourIndex] = glyph->endPoint[lContourIndex - 1] + 1; + + unsignedWord = READALIGNEDWORD(sp); + glyph->endPoint[lContourIndex] = SWAPW(unsignedWord); + + if (numKnots > glyph->endPoint[lContourIndex] || numKnots <= 0) + { + return false; + } + numKnots = glyph->endPoint[lContourIndex] + 1; + } + } + + unsignedWord = READALIGNEDWORD(sp); + binSize = SWAPW(unsignedWord); + + if (binSize > MAXBINSIZE) { + swprintf(errMsg, errMsgLen, L"GetGlyph: glyph %li has %li bytes of instruction (cannot have more than %li)", glyphIndex, binSize, MAXBINSIZE); return false; + } + + this->UpdateBinData(asmGLYF, binSize, sp); + sp += binSize; + + // flags + + unsigned short usRepeatCount = 0; + uint8 byRepeatFlag; + + int32 lPointCount = numKnots; + uint8* pbyFlags = abyOnCurve; + + while (lPointCount > 0) + { + if (usRepeatCount == 0) + { + + *pbyFlags = *sp; + + if (*pbyFlags & REPEAT_FLAGS) + { + sp++; + + usRepeatCount = *sp; + } + sp++; + pbyFlags++; + lPointCount--; + } + else + { + byRepeatFlag = pbyFlags[-1]; + lPointCount -= usRepeatCount; + + if (lPointCount < 0) + { + return false; + } + + while (usRepeatCount > 0) + { + *pbyFlags = byRepeatFlag; + pbyFlags++; + usRepeatCount--; + } + } + } + + if (usRepeatCount > 0) + { + return false; + } + + // X + short sXValue = 0; + F26Dot6* pf26OrigX = glyph->xx; + pbyFlags = abyOnCurve; + + for (int32 lPointIndex = 0; lPointIndex < numKnots; lPointIndex++) + { + if (*pbyFlags & XSHORT) + { + if (*pbyFlags & SHORT_X_IS_POS) + { + sXValue += (int16)((uint8)*sp++); + } + else + { + sXValue -= (int16)((uint8)*sp++); + } + } + else if (!(*pbyFlags & NEXT_X_IS_ZERO)) + { + // two bytes + sXValue += SWAPW(*((int16*)sp)); + sp += sizeof(int16); + } + *pf26OrigX = (F26Dot6)sXValue; + pf26OrigX++; + pbyFlags++; + } + + // Y + short sYValue = 0; + F26Dot6* pf26OrigY = glyph->yy; + pbyFlags = abyOnCurve; + + for (int32 lPointIndex = 0; lPointIndex < numKnots; lPointIndex++) + { + if (*pbyFlags & YSHORT) + { + if (*pbyFlags & SHORT_Y_IS_POS) + { + sYValue += (int16)((uint8)*sp++); + } + else + { + sYValue -= (int16)((uint8)*sp++); + } + } + else if (!(*pbyFlags & NEXT_Y_IS_ZERO)) + { + // two bytes + sYValue += SWAPW(*((int16*)sp)); + sp += sizeof(int16); + } + *pf26OrigY = (F26Dot6)sYValue; + pf26OrigY++; + + *pbyFlags &= ONCURVE; + glyph->onCurve[lPointIndex] = *pbyFlags & ONCURVE; + pbyFlags++; + } + } + + end = glyph->endPoint[glyph->numContoursInGlyph - 1]; + + F26Dot6 fxXMinMinusLSB = glyph->xmin - lsb; + glyph->xx[end + 1 + LEFTSIDEBEARING] = fxXMinMinusLSB; + glyph->xx[end + 1 + RIGHTSIDEBEARING] = fxXMinMinusLSB + advWidth; + glyph->yy[end + 1 + LEFTSIDEBEARING] = 0; + glyph->yy[end + 1 + RIGHTSIDEBEARING] = 0; + + for (i = 0; i <= end + PHANTOMPOINTS; i++) + { // lsb, rsb + glyph->x[i] = (short)(glyph->xx[i]); // >> places6); + glyph->y[i] = (short)(glyph->yy[i]); // >> places6); + } + + this->UpdateGlyphProfile(glyph); + this->UpdateMetricProfile(glyph); + + return true; // by now +} // TrueTypeFont::GetGlyph + +int32_t TrueTypeFont::GlyfBinSize(void) { + return this->binSize[asmGLYF]; +} // TrueTypeFont::GlyfBinSize + +unsigned char* TrueTypeFont::GlyfBin(void) { + return this->binData[asmGLYF]; +} + +bool TrueTypeFont::GetHMTXEntry(int32_t glyphIndex, int32_t *leftSideBearing, int32_t *advanceWidth) { + sfnt_HorizontalMetrics *horMetric; + + *leftSideBearing = *advanceWidth = 0; + + if (glyphIndex < 0 || this->numberOfGlyphs <= glyphIndex) glyphIndex = 0; // map to missing glyph + + horMetric = &this->horMetric[glyphIndex]; + *leftSideBearing = horMetric->leftSideBearing; + *advanceWidth = horMetric->advanceWidth; + + return true; // by now +} // TrueTypeFont::GetHMTXEntry + +int32_t TrueTypeFont::NumberOfGlyphs(void) { + return Min(this->GetTableLength(tag_IndexToLoc)/(this->shortIndexToLocTable ? sizeof(short) : sizeof(int32_t)) - 1,this->profile.numGlyphs); + // in a correct font, there are this->profile.numGlyphs + 1 (ie. maxp->numGlyphs + 1) entries in the 'loca' table, however, according to + // claudebe there are still lots of fonts that have an incorrect size for the loca table, so it is safe to use the minimum of the two. +} // TrueTypeFont::NumberOfGlyphs + +bool Compare_UniGlyphMap(UniGlyphMap first, UniGlyphMap second) +{ + // Comparison function that, taking two values of the same type than those + // contained in the list object, returns true if the first argument goes before the + // second argument in the specific order, and false otherwise. + + return (first.unicode < second.unicode); +} + +int32_t TrueTypeFont::GlyphIndexOf(uint32_t charCode) { + std::vector::iterator it; + UniGlyphMap map; + map.unicode = charCode; + + if(!std::binary_search(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap)) + return INVALID_GLYPH_INDEX; + + it = std::lower_bound(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap); + + return it->glyphIndex; +} // TrueTypeFont::GlyphIndexOf + +bool TrueTypeFont::GlyphIndecesOf(wchar_t textString[], int32_t maxNumGlyphIndeces, int32_t glyphIndeces[], int32_t *numGlyphIndeces, wchar_t errMsg[], size_t errMsgLen) { + int32_t i,dec,hex; + wchar_t ch; + + errMsg[0] = (char)0; + i = *numGlyphIndeces = 0; ch = textString[i++]; + while (ch != 0 && *numGlyphIndeces < maxLineSize-1) { + if (ch == L'^') { // (decimal) glyphIndex + ch = textString[i++]; dec = 0; + while (ch && ch != L'^') { + if (L'0' <= ch && ch <= L'9') dec = 10*dec + (int32_t)(ch - L'0'); + else + { + swprintf(errMsg, errMsgLen, L"invalid decimal digit in glyph index"); + return false; + } + ch = textString[i++]; + } + if (ch == L'^') ch = textString[i++]; + else + { + swprintf(errMsg, errMsgLen, L"closing ^ missing"); + return false; + } + } else if (ch == L'~') { // (hexadecimal) charCode + ch = textString[i++]; hex = 0; + while (ch && ch != L'~') { + if (L'0' <= ch && ch <= L'9') hex = 16*hex + (int32_t)(ch - L'0'); + else if (L'A' <= ch && ch <= L'F') hex = 16*hex + (int32_t)(ch - L'A' + 10); + else if (L'a' <= ch && ch <= L'f') hex = 16*hex + (int32_t)(ch - L'a' + 10); + else + { + swprintf(errMsg, errMsgLen, L"invalid hexadecimal digit in glyph index"); + return false; + } + ch = textString[i++]; + } + if (ch == L'~') ch = textString[i++]; + else + { + swprintf(errMsg, errMsgLen, L"closing ~ missing"); + return false; + } + dec = this->GlyphIndexOf(hex); + } else { + dec = this->GlyphIndexOf(ch); + ch = textString[i++]; + } + if (dec < 0 || this->numberOfGlyphs <= dec) dec = 0; // map to missing glyph + glyphIndeces[(*numGlyphIndeces)++] = dec; + } + + return true; // by now +} // TrueTypeFont::GlyphIndecesOf + +uint32_t TrueTypeFont::CharCodeOf(int32_t glyphIndex) { + return this->charCodeOf[glyphIndex]; +} // TrueTypeFont::CharCodeOf + +uint32_t TrueTypeFont::FirstChar() { + return (glyphIndexMap->begin()->unicode); +} + +uint32_t TrueTypeFont::AdjacentChar(uint32_t charCode, bool forward) { + uint32_t returnCode = 0; + uint32_t size = (uint32_t)glyphIndexMap->size(); + UniGlyphMap map; + map.unicode = charCode; + std::vector::iterator it; + + if(std::binary_search(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap)) { + it = std::lower_bound(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap); + int32_t delta = forward ? 1 : size - 1; + returnCode = glyphIndexMap->at(((it - glyphIndexMap->begin()) + delta) % size).unicode; + }else { + it = std::lower_bound(glyphIndexMap->begin(), glyphIndexMap->end(), map, Compare_UniGlyphMap); + int32_t delta = forward ? 0 : size - 1; + returnCode = glyphIndexMap->at(((it - glyphIndexMap->begin()) + delta) % size).unicode; + } + + return returnCode; +} // TrueTypeFont::AdjacentChar + +CharGroup TrueTypeFont::CharGroupOf(int32_t glyphIndex) { + if (glyphIndex < 0 || this->profile.numGlyphs <= glyphIndex) glyphIndex = 0; // safety measure, just in case + return (CharGroup)this->charGroupOf[glyphIndex]; +} // TrueTypeFont::CharGroupOf + +bool TrueTypeFont::CMapExists(short platformID, short encodingID) { + sfnt_char2IndexDirectory *cmap = (sfnt_char2IndexDirectory *)this->GetTablePointer(tag_CharToIndexMap); + short i; + int num = SWAPW(cmap->numTables); + + platformID = SWAPW(platformID); + encodingID = SWAPW(encodingID); + for (i = 0; i < num && (cmap->platform[i].platformID != platformID || cmap->platform[i].specificID != encodingID); i++); + return i < num; +} // TrueTypeFont::CMapExists + +bool TrueTypeFont::DefaultCMap(short *platformID, short *encodingID, wchar_t errMsg[], size_t errMsgLen) { + sfnt_char2IndexDirectory *cmap = (sfnt_char2IndexDirectory *)this->GetTablePointer(tag_CharToIndexMap); + int32_t i,num = SWAPW(cmap->numTables); + + if (num <= 0) + { + swprintf(errMsg, errMsgLen, L"There is no cmap in this font"); + return false; + } + *platformID = CSWAPW(plat_MS); *encodingID = CSWAPW(10); + for (i = 0; i < num && (cmap->platform[i].platformID != *platformID || cmap->platform[i].specificID != *encodingID); i++); + if (i < num) goto found; // found preferred cmap... + *encodingID = CSWAPW(1); + for (i = 0; i < num && (cmap->platform[i].platformID != *platformID || cmap->platform[i].specificID != *encodingID); i++); + if (i < num) goto found; // found preferred cmap... + for (i = 0; i < num && cmap->platform[i].platformID != *platformID; i++); + if (i < num) { *encodingID = cmap->platform[i].specificID; goto found; } // found cmap in preferred platform... + *platformID = cmap->platform[0].platformID; + *encodingID = cmap->platform[0].specificID; +found: + *platformID = SWAPW(*platformID); *encodingID = SWAPW(*encodingID); + return true; // found any cmap at all +} // TrueTypeFont::DefaultCMap + +bool TrueTypeFont::UnpackCMap(short platformID, short encodingID, wchar_t errMsg[], size_t errMsgLen) { + int32_t i,num; + sfnt_char2IndexDirectory *cmap; + sfnt_mappingTable *map; + + for (i = 0; i < this->maxGlitEntries; i++) this->charCodeOf[i] = 0xFFFF; + this->glyphIndexMap->clear(); + this->numberOfGlyphs = this->NumberOfGlyphs(); + this->numberOfChars = 0; // incremented in EnterChar above... + + cmap = (sfnt_char2IndexDirectory*)this->GetTablePointer(tag_CharToIndexMap); + num = SWAPW(cmap->numTables); + + platformID = SWAPW(platformID); + encodingID = SWAPW(encodingID); + + for (i = 0; i < num && (cmap->platform[i].platformID != platformID || cmap->platform[i].specificID != encodingID); i++); + if (i == num) + { + swprintf(errMsg, errMsgLen, L"Unpacking cmap: cmap for platform id %hi and encoding id %hi not found", platformID, encodingID); + return false; + } + + map = (sfnt_mappingTable*)(reinterpret_cast(cmap) + SWAPL(cmap->platform[i].offset)); + uint16 format = SWAPW(map->format); + switch (format) { + case 0: this->GetFmt0(map); break; + case 4: this->GetFmt4(map); break; + case 6: this->GetFmt6(map); break; + case 12: this->GetFmt12(map); break; + default: + swprintf(errMsg, errMsgLen, L"Unpacking cmap: cmap format %hi not implemented", map->format); + return false; + } + return true; // by now +} // TrueTypeFont::UnpackCMap + +bool TrueTypeFont::IsCvarTupleData() +{ + uint16_t recordCount = 0; + + if (this->IsVariationFont()) + { + auto instanceManager = this->GetInstanceManager(); + auto cvarTuples = instanceManager->GetCvarTuples(); + + for (auto cvarTuplesIt = cvarTuples->cbegin(); cvarTuplesIt != cvarTuples->cend(); ++cvarTuplesIt) + { + recordCount += (uint16_t)(*cvarTuplesIt)->cvt.size(); + } + } + + return recordCount > 0; +} + +int32_t TrueTypeFont::EstimatePrivateCvar() +{ + int32_t size = 0; + + if (!this->IsVariationFont()) + return size; + + auto instanceManager = this->GetInstanceManager(); + auto tsicInstances = instanceManager->GetPrivateCvarInstances(); + auto axisCount = this->GetVariationAxisCount(); + + bool editedValues = false; + // Any edited values? + for (const auto & instance : *tsicInstances) + { + for (const auto & editedValue : instance->editedCvtValues) + { + if (!editedValues && editedValue.Edited()) + { + editedValues = true; + } + } + } + + // Anything to do? + if (!editedValues) + return size; + + size += sizeof(uint16_t); // majorVersion + size += sizeof(uint16_t); // minorVersion + size += sizeof(uint16_t); // flags + size += sizeof(uint16_t); // axisCount + size += sizeof(uint16_t); // recordCount + size += sizeof(uint16_t); // reserved + + size += sizeof(uint32_t) * axisCount; // AxisArray + size += sizeof(uint16_t) * static_cast(tsicInstances->size()) * axisCount; //RecordLocations + + // TSICRecords + for (const auto & instance : *tsicInstances) + { + size += sizeof(uint16_t); // flags + size += sizeof(uint16_t); // numCVTEntries + size += sizeof(uint16_t); // nameLength + std::wstring name = instance->GetName(); + size += static_cast(name.size() * sizeof(wchar_t)); + + for (const auto & editedValue : instance->editedCvtValues) + { + if (editedValue.Edited()) + { + size += sizeof(uint16_t); // CVTArray entry + size += sizeof(int16_t); // CVTValueArray entry + } + } + } + + return size; +} + +int32_t TrueTypeFont::UpdatePrivateCvar(int32_t *size, unsigned char data[]) +{ + unsigned short *packed = reinterpret_cast(data); + + if (!this->IsVariationFont()) + { + *size = 0; + return *size; + } + + auto instanceManager = this->GetInstanceManager(); + auto tsicInstances = instanceManager->GetPrivateCvarInstances(); + + bool editedValues = false; + // Any edited values? + for (const auto & instance : *tsicInstances) + { + for (const auto & editedValue : instance->editedCvtValues) + { + if (!editedValues && editedValue.Edited()) + { + editedValues = true; + } + } + } + + // Anything to do? + if (!editedValues) + { + *size = 0; + return *size; + } + + TSICHeader header; + header.majorVersion = 1; + // minorVersion 0 tags are mistakenly swapped + // minorVersion 1 tags are not swapped + header.minorVersion = 1; + header.flags = 0; + header.axisCount = this->GetVariationAxisCount(); + header.recordCount = static_cast(tsicInstances->size()); + header.reserved = 0; + auto variationAxisTags = this->GetVariationAxisTags(); + + *packed++ = SWAPW(header.majorVersion); + *packed++ = SWAPW(header.minorVersion); + *packed++ = SWAPW(header.flags); + *packed++ = SWAPW(header.axisCount); + *packed++ = SWAPW(header.recordCount); + *packed++ = SWAPW(header.reserved); + + assert(header.axisCount == variationAxisTags->size()); + // AxisArray + for (const auto & tag : *variationAxisTags) + { + uint32_t* p = reinterpret_cast(packed); + *p = tag; + packed += 2; + } + + // RecordLocations + for (const auto & instance : *tsicInstances) + { + assert(instance->peakF2Dot14.size() == header.axisCount); + for (const auto & axisCoord : instance->peakF2Dot14) + { + uint16_t coord = axisCoord.GetRawValue(); + *packed++ = SWAPW(coord); + } + } + + // TSICRecords + for (const auto & instance : *tsicInstances) + { + TSICRecord record; + record.flags = 0; + + record.numCVTEntries = 0; + for (const auto & editedValue : instance->editedCvtValues) + { + if (editedValue.Edited()) + { + record.numCVTEntries++; + } + } + + *packed++ = SWAPW(record.flags); + *packed++ = SWAPW(record.numCVTEntries); + + record.nameLength = 0; + std::wstring name = instance->GetName(); + if (!instance->IsNamedInstance() && !this->IsMakeTupleName(name)) + { + record.nameLength = static_cast(name.size()); + *packed++ = SWAPW(record.nameLength); + + // NameArray + for (const auto & character : name) + { + *packed++ = SWAPW(character); + } + } + else + { + *packed++ = SWAPW(record.nameLength); + } + + // CVTArray + uint16_t cvt = 0; + for (const auto & editedValue : instance->editedCvtValues) + { + if (editedValue.Edited()) + { + *packed++ = SWAPW(cvt); + } + cvt++; + } + + // CVTValueArray + for (const auto & editedValue : instance->editedCvtValues) + { + if (editedValue.Edited()) + { + int16_t value = editedValue.Value(); + *packed++ = SWAPW(value); + } + } + } + + unsigned char* end = reinterpret_cast(packed); + *size = static_cast(end - data); + + return *size; +} + +bool TrueTypeFont::HasPrivateCvar() +{ + const int32_t privateCvarMinSize = 10 * sizeof(uint16_t) + sizeof(uint32_t); + + int32_t tableLength = this->GetTableLength(PRIVATE_CVAR); + + return(tableLength > privateCvarMinSize && !this->tsicError); +} + +bool TrueTypeFont::GetPrivateCvar(TSICHeader &header) +{ + int32_t tableLength = this->GetTableLength(PRIVATE_CVAR); + + if (tableLength == 0 || !this->IsVariationFont()) + return true; + + unsigned short *packed = reinterpret_cast(this->GetTablePointer(PRIVATE_CVAR)); + if (packed == nullptr) + return true; + + auto tags = this->GetVariationAxisTags(); + if (tags == nullptr) + return true; + + header.majorVersion = SWAPWINC(packed); + header.minorVersion = SWAPWINC(packed); + header.flags = SWAPWINC(packed); + header.axisCount = SWAPWINC(packed); + header.recordCount = SWAPWINC(packed); + header.reserved = SWAPWINC(packed); + + if (header.axisCount != this->GetVariationAxisCount()) + return false; + + // AxisArray + assert(tags->size() == header.axisCount); + for (uint16_t axisIndex = 0; axisIndex < header.axisCount; axisIndex++) + { + uint32_t* ptag = reinterpret_cast(packed); + uint32_t tag = *ptag; + uint32_t swapTag = SWAPL(*ptag); + header.axes.push_back(tag); + if (tag != tags->at(axisIndex) && swapTag != tags->at(axisIndex)) + return false; + + packed += 2; + } + + // RecordLocations + for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) + { + std::vector coords; + for (uint16_t axisIndex = 0; axisIndex < header.axisCount; axisIndex++) + { + uint16_t cval = SWAPWINC(packed); + Fixed2_14 coord(cval); + coords.push_back(coord); + } + header.locations.push_back(coords); + } + + // TSICRecords + for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) + { + TSICRecord record; + record.name = L""; + + record.flags = SWAPWINC(packed); + record.numCVTEntries = SWAPWINC(packed); + record.nameLength = SWAPWINC(packed); + + // NameArray + for (uint16_t nameIndex = 0; nameIndex < record.nameLength; nameIndex++) + { + wchar_t character = SWAPWINC(packed); + record.name += character; + } + + // CVTArray + for (uint16_t cvtIndex = 0; cvtIndex < record.numCVTEntries; cvtIndex++) + { + uint16_t cvt = SWAPWINC(packed); + record.cvts.push_back(cvt); + } + + // CVTValueArray + for (uint16_t cvtIndex = 0; cvtIndex < record.numCVTEntries; cvtIndex++) + { + int16_t cvtValue = SWAPWINC(packed); + record.cvtValues.push_back(cvtValue); + } + + header.tsicRecords.push_back(record); + } + + return true; +} + +bool TrueTypeFont::MergePrivateCvarWithInstanceManager(const TSICHeader &header) +{ + if (!this->IsVariationFont()) + return true; + + auto instanceManager = this->GetInstanceManager(); + // auto axisCount = this->GetVariationAxisCount(); + + std::vector recordMerged; + recordMerged.resize(header.recordCount, false); + + // For each record in private see if there exists same location among instances. + for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) + { + // Get the location + std::vector location = header.locations.at(recordIndex); + // Get the cooresponding Record + const TSICRecord &record = header.tsicRecords[recordIndex]; + assert(record.cvts.size() == record.cvtValues.size()); + + for (uint16_t instanceIndex = 0; instanceIndex < instanceManager->size(); instanceIndex++) + { + Variation::Instance& instance = instanceManager->at(instanceIndex); + std::vector instLoc = instance.GetNormalizedFixed2_14Coordinates(); + if (Variation::IsFixed2_14CoordEqual(location, instLoc)) + { + recordMerged.at(recordIndex) = true; + + // Copy source cvt data to instance + if (record.numCVTEntries > 0) + { + uint16_t highestCvt = 0; + for (const auto & cvt : record.cvts) + highestCvt = std::max(cvt, highestCvt); + + instance.editedCvtValues.clear(); + instance.editedCvtValues.resize(highestCvt + 1, Variation::EditedCvtValue()); + + for (uint16_t recordArrayIndex = 0; recordArrayIndex < record.cvts.size(); recordArrayIndex++) + { + instance.editedCvtValues.at(record.cvts.at(recordArrayIndex)).Update(record.cvtValues.at(recordArrayIndex)); + } + instance.SetAsCvar(true); + } + + // Copy name to instance + if (record.nameLength > 0) + { + instance.SetName(record.name); + } + } + } + } + + // Deal with left over records + for (uint16_t recordIndex = 0; recordIndex < header.recordCount; recordIndex++) + { + if (recordMerged.at(recordIndex) == false) + { + // Get the location + std::vector location = header.locations.at(recordIndex); + // Get the cooresponding Record + const TSICRecord &record = header.tsicRecords[recordIndex]; + assert(record.cvts.size() == record.cvtValues.size()); + + Variation::Instance instance(location); + + // Copy source cvt data to instance + if (record.numCVTEntries > 0) + { + uint16_t highestCvt = 0; + for (const auto & cvt : record.cvts) + highestCvt = std::max(cvt, highestCvt); + + instance.editedCvtValues.resize(highestCvt + 1, Variation::EditedCvtValue()); + + for (uint16_t recordArrayIndex = 0; recordArrayIndex < record.cvts.size(); recordArrayIndex++) + { + instance.editedCvtValues.at(record.cvts.at(recordArrayIndex)).Update(record.cvtValues.at(recordArrayIndex)); + } + instance.SetAsCvar(true); + } + + // Copy name to instance + if (record.nameLength > 0) + { + instance.SetName(record.name); + } + + instanceManager->Add(instance); + } + } + + return true; +} + +int32_t TrueTypeFont::EstimateCvar() +{ + int32_t size = 0; + + if (!this->IsVariationFont()) + return size; + + auto instanceManager = this->GetInstanceManager(); + auto cvarTuples = instanceManager->GetCvarTuples(); + auto axisCount = this->GetVariationAxisCount(); + size_t numTuplesWithData = 0; + size_t numIntermediates = 0; // Among numTuplesWithData how many intermediates + + // Determine how many tuples have data and how many of those have intermediates + for (const auto & tuple : *cvarTuples) + { + if (tuple->cvt.size() > 0) + { + numTuplesWithData += 1; + if (tuple->intermediateStartF2Dot14.size() > 0 || tuple->intermediateEndF2Dot14.size() > 0) + { + numIntermediates += 1; + } + } + } + size = (int32_t)(sizeof(CvarHeader) + // cvar header + numTuplesWithData * (2 * sizeof(USHORT) + axisCount * sizeof(USHORT)) + // TupleVariationHeader for tuples with data + numIntermediates * (2 * axisCount * sizeof(USHORT))); // TupleVariationHeader addition for entries with intermediates (start and end) + + for (const auto & tuple : *cvarTuples) + { + // no data then skip this tuple + if (tuple->cvt.size() == 0) + continue; + + size += (int32_t)(tuple->cvt.size() * 2 * sizeof(USHORT) + 2 * sizeof(USHORT)); + } + + return (numTuplesWithData > 0) ? size : 0; +} + +static bool Compare_Tuples_by_Order(const Variation::CvarTuple & first, const Variation::CvarTuple & second) +{ + // Comparison function that, taking two values of the same type than those + // contained in the list object, returns true if the first argument goes before the + // second argument in the specific order, and false otherwise. + return first.GetWriteOrder() < second.GetWriteOrder(); +} + +int32_t TrueTypeFont::UpdateCvar(int32_t *size, unsigned char data[]) +{ + CvarHeader header; + unsigned short *packed = (unsigned short*)data; + + if (!this->IsVariationFont()) + { + *size = 0; + return *size; + } + + // Copy tuples and then sort according to embedded order. + auto instanceManager = this->GetInstanceManager(); + auto cvarTuples = instanceManager->GetCvarTuplesCopy(); + std::stable_sort(cvarTuples.begin(), cvarTuples.end(), Compare_Tuples_by_Order); + + auto axisCount = this->GetVariationAxisCount(); + // auto tupleCount = cvarTuples.size(); + size_t numTuplesWithData = 0; + size_t numIntermediates = 0; // Among numTuplesWithData how many intermediates + + // Determine how many tuples have data and how many of those have intermediates + for (const auto & tuple : cvarTuples) + { + if (tuple.cvt.size() > 0) + { + numTuplesWithData += 1; + if (tuple.intermediateStartF2Dot14.size() > 0 || tuple.intermediateEndF2Dot14.size() > 0) + { + numIntermediates += 1; + } + } + } + + // if no tuples have data we don't need a cvar table + if (numTuplesWithData == 0) + { + *size = 0; + return *size; + } + + header.majorVersion = 1; + header.minorVersion = 0; + header.tupleVariationCount = (unsigned short)numTuplesWithData; // no flags + header.offsetToData = (unsigned short)(sizeof(CvarHeader) + // cvar header + numTuplesWithData * (2 * sizeof(USHORT) + axisCount * sizeof(USHORT)) + // TupleVariationHeader for tuples with data + numIntermediates * (2 * axisCount * sizeof(USHORT))); // TupleVariationHeader addition for entries with intermediates (start and end) + + *packed++ = SWAPW(header.majorVersion); + *packed++ = SWAPW(header.minorVersion); + *packed++ = SWAPW(header.tupleVariationCount); + *packed++ = SWAPW(header.offsetToData); + + unsigned char* packedByteData = data + header.offsetToData; + unsigned short byteOffset = 0; + + // write serialized data + for (auto cvarTuplesIt = cvarTuples.cbegin(); cvarTuplesIt != cvarTuples.cend(); ++cvarTuplesIt) + { + // no data then skip this tuple + if(cvarTuplesIt->cvt.size() == 0) + continue; + + uint16_t previousByteOffset = byteOffset; + uint16_t recordCount = (uint16_t)cvarTuplesIt->cvt.size(); + uint16_t cvtsToWrite = recordCount; + if (recordCount > SCHAR_MAX) + { + USHORT* packedWordData = (unsigned short*)&packedByteData[byteOffset]; + USHORT c = 0x8000 | recordCount; + *packedWordData = SWAPW(c); + byteOffset += sizeof(USHORT); + } + else + { + unsigned char c = (unsigned char)recordCount; + packedByteData[byteOffset] = c; + byteOffset += sizeof(unsigned char); + } + + // determine highest cvt + uint16_t highestCvt = 0; + for (int cIndex = 0; cIndex < recordCount; ++cIndex) + highestCvt = Max(cvarTuplesIt->cvt[cIndex], highestCvt); + + // write cvt numbers + // break into runs if needed + uint16_t cvtIndex = 0; + uint16_t currentCvt = 0; + while(cvtsToWrite > 0) + { + uint16_t runWrite = Min(cvtsToWrite, POINT_RUN_COUNT_MASK); + + // write control byte for run + unsigned char c = (unsigned char)runWrite - 1; + if (highestCvt > UCHAR_MAX) + c |= POINTS_ARE_WORDS; + packedByteData[byteOffset] = c; + byteOffset += sizeof(unsigned char); + + if (highestCvt <= UCHAR_MAX) + { + // use bytes + for (int wIndex = 0; wIndex < runWrite; ++wIndex) + { + packedByteData[byteOffset] = cvarTuplesIt->cvt[cvtIndex] - currentCvt; + currentCvt = cvarTuplesIt->cvt[cvtIndex++]; + byteOffset += sizeof(unsigned char); + } + } + else + { + // use words + for (int wIndex = 0; wIndex < runWrite; ++wIndex) + { + USHORT* packedWordData = (unsigned short*)&packedByteData[byteOffset]; + uint16_t temp = cvarTuplesIt->cvt[cvtIndex] - currentCvt; + *packedWordData = SWAPW(temp); + currentCvt = cvarTuplesIt->cvt[cvtIndex++]; + byteOffset += sizeof(USHORT); + } + } + cvtsToWrite -= runWrite; + } + + // write deltas + auto deltasToWrite = recordCount; + uint16_t largestDelta = 0; + for (int dIndex = 0; dIndex < recordCount; ++dIndex) + largestDelta = Max(std::abs(cvarTuplesIt->delta[dIndex]), largestDelta); + + cvtIndex = 0; + while (deltasToWrite > 0) + { + // write control byte for run + uint16_t runWrite = Min(deltasToWrite, DELTA_RUN_COUNT_MASK); + unsigned char c = (unsigned char)runWrite - 1; + if (largestDelta > SCHAR_MAX) + c |= DELTAS_ARE_WORDS; + packedByteData[byteOffset] = c; + byteOffset += sizeof(unsigned char); + + if (largestDelta <= SCHAR_MAX) + { + // use bytes + for (int wIndex = 0; wIndex < runWrite; ++wIndex) + { + packedByteData[byteOffset] = (unsigned char)cvarTuplesIt->delta[cvtIndex++]; + byteOffset += sizeof(unsigned char); + } + } + else + { + // use words + for (int wIndex = 0; wIndex < runWrite; ++wIndex) + { + USHORT delta = cvarTuplesIt->delta[cvtIndex++]; + USHORT* packedWordData = (unsigned short*)&packedByteData[byteOffset]; + *packedWordData = SWAPW(delta); + byteOffset += sizeof(USHORT); + } + } + deltasToWrite -= runWrite; + } + + // write header entry for this tuple + uint16_t variationDataSize = byteOffset - previousByteOffset; + *packed++ = SWAPW(variationDataSize); + + // If you have one you need both! + assert(cvarTuplesIt->intermediateEndF2Dot14.size() == cvarTuplesIt->intermediateStartF2Dot14.size()); + + bool intermediate = cvarTuplesIt->intermediateEndF2Dot14.size() > 0 || cvarTuplesIt->intermediateStartF2Dot14.size() > 0; + uint16_t tupleIndex = 0x8000 | 0x2000; + if (intermediate) + tupleIndex = tupleIndex | 0x4000; + + *packed++ = SWAPW(tupleIndex); + + // Write the peakTuple + for (size_t coordIndex = 0; coordIndex < cvarTuplesIt->peakF2Dot14.size(); ++coordIndex) + { + uint16_t coord = cvarTuplesIt->peakF2Dot14[coordIndex].GetRawValue(); + *packed++ = SWAPW(coord); + } + + if (intermediate) + { + // Write the intermediateStartTuple + for (size_t coordIndex = 0; coordIndex < cvarTuplesIt->intermediateStartF2Dot14.size(); ++coordIndex) + { + uint16_t coord = cvarTuplesIt->intermediateStartF2Dot14[coordIndex].GetRawValue(); + *packed++ = SWAPW(coord); + } + + // Write the intermediateEndTuple + for (size_t coordIndex = 0; coordIndex < cvarTuplesIt->intermediateEndF2Dot14.size(); ++coordIndex) + { + uint16_t coord = cvarTuplesIt->intermediateEndF2Dot14[coordIndex].GetRawValue(); + *packed++ = SWAPW(coord); + } + } + } + + *size = header.offsetToData + byteOffset; + + return *size; +} + +void TrueTypeFont::UpdateAdvanceWidthFlag(bool linear) { + sfnt_FontHeader *head; + uint16 flags; + + head = (sfnt_FontHeader*)this->GetTablePointer(tag_FontHeader); + flags = SWAPW(head->flags); + if (linear) flags &= ~USE_INTEGER_SCALING; else flags |= USE_INTEGER_SCALING; + head->flags = SWAPW(flags); +} // TrueTypeFont::UpdateAdvanceWidthFlag + +#define OPTIMIZED_FOR_CLEARTYPE 0x2000 + +bool TrueTypeFont::UpdateBinData(ASMType asmType, int32_t binSize, unsigned char *binData) { + unsigned char *binTemp; + + if (asmType < firstASMType || lastASMType < asmType || binSize < 0) return false; + + binTemp = binSize > 0 ? (unsigned char *)NewP(binSize) : NULL; + if (binSize > 0 && binTemp == NULL) return false; + + if (this->binData[asmType] != NULL) DisposeP((void **)&this->binData[asmType]); + + if (binSize > 0 && binData != NULL) memcpy(binTemp,binData,binSize); + this->binSize[asmType] = binSize; + this->binData[asmType] = binTemp; + + return true; +} // TrueTypeFont::UpdateBinData + +bool TrueTypeFont::TableExists(sfnt_TableTag tag) { + sfnt_OffsetTable *p; + int32_t num,i; + + p = (sfnt_OffsetTable *)this->sfntHandle; + num = SWAPW(p->numOffsets); + tag = SWAPL(tag); + for (i = 0; i < num && p->table[i].tag != tag; i++); + return i < num; // found +} // TrueTypeFont::TableExists + +int32_t TrueTypeFont::GetTableOffset(sfnt_TableTag tag) { + sfnt_OffsetTable *p; + int32_t num,i; + + p = (sfnt_OffsetTable *)this->sfntHandle; + num = SWAPW(p->numOffsets); + tag = SWAPL(tag); + for (i = 0; i < num && p->table[i].tag != tag; i++); + return ((i < num) && (SWAPL(p->table[i].length) > 0)) ? SWAPL(p->table[i].offset) : 0; +} // TrueTypeFont::GetTableOffset + +int32_t TrueTypeFont::GetTableLength(sfnt_TableTag tag) { + sfnt_OffsetTable *p; + int32_t num,i; + + p = (sfnt_OffsetTable *)this->sfntHandle; + num = SWAPW(p->numOffsets); + tag = SWAPL(tag); + for (i = 0; i < num && p->table[i].tag != tag; i++); + return (i < num) ? SWAPL(p->table[i].length) : 0; +} // TrueTypeFont::GetTableLength + +unsigned char *TrueTypeFont::GetTablePointer(sfnt_TableTag tag) { + int32_t offs = this->GetTableOffset(tag); + return offs != 0 ? this->sfntHandle + offs : NULL; +// return *this->sfntHandle + this->GetTableOffset(tag); +} // TrueTypeFont::GetTablePointer + +void UnpackMaxp(unsigned char *sfnt, sfnt_maxProfileTable *profile) { + sfnt_maxProfileTable *maxp = (sfnt_maxProfileTable *)sfnt; + + profile->version = SWAPL(maxp->version); // for this table, set to 1.0 + profile->numGlyphs = SWAPW(maxp->numGlyphs); + profile->maxPoints = SWAPW(maxp->maxPoints); + profile->maxContours = SWAPW(maxp->maxContours); + profile->maxCompositePoints = SWAPW(maxp->maxCompositePoints); + profile->maxCompositeContours = SWAPW(maxp->maxCompositeContours); + profile->maxElements = SWAPW(maxp->maxElements); + profile->maxTwilightPoints = SWAPW(maxp->maxTwilightPoints); + profile->maxStorage = SWAPW(maxp->maxStorage); + profile->maxFunctionDefs = SWAPW(maxp->maxFunctionDefs); + profile->maxInstructionDefs = SWAPW(maxp->maxInstructionDefs); + profile->maxStackElements = SWAPW(maxp->maxStackElements); + profile->maxSizeOfInstructions = SWAPW(maxp->maxSizeOfInstructions); + profile->maxComponentElements = SWAPW(maxp->maxComponentElements); + profile->maxComponentDepth = SWAPW(maxp->maxComponentDepth); +} // UnpackMaxp + +void PackMaxp(unsigned char *sfnt, sfnt_maxProfileTable *profile) { + sfnt_maxProfileTable *maxp = (sfnt_maxProfileTable *)sfnt; + + maxp->version = SWAPL(profile->version); + maxp->numGlyphs = SWAPW(profile->numGlyphs); + maxp->maxPoints = SWAPW(profile->maxPoints); + maxp->maxContours = SWAPW(profile->maxContours); + maxp->maxCompositePoints = SWAPW(profile->maxCompositePoints); + maxp->maxCompositeContours = SWAPW(profile->maxCompositeContours); + maxp->maxElements = SWAPW(profile->maxElements); + maxp->maxTwilightPoints = SWAPW(profile->maxTwilightPoints); + maxp->maxStorage = SWAPW(profile->maxStorage); + maxp->maxFunctionDefs = SWAPW(profile->maxFunctionDefs); + maxp->maxInstructionDefs = SWAPW(profile->maxInstructionDefs); + maxp->maxStackElements = SWAPW(profile->maxStackElements); + maxp->maxSizeOfInstructions = SWAPW(profile->maxSizeOfInstructions); + maxp->maxComponentElements = SWAPW(profile->maxComponentElements); + maxp->maxComponentDepth = SWAPW(profile->maxComponentDepth); +} // PackMaxp + +bool TrueTypeFont::UnpackHeadHheaMaxpHmtx(wchar_t errMsg[], size_t errMsgLen) { + sfnt_FontHeader *phead, head; + sfnt_HorizontalHeader *phhea, hhea; + unsigned short aw,lsb,*hmtx; // actually hhea->numberOf_LongHorMetrics of sfnt_HorizontalMetrics [aw,lsb] pairs the monospaced part where we have only lsb numbers + int32_t glitLen,numEntries,numGlyphs,i,k; + + unsigned char* pmaxp = this->GetTablePointer(tag_MaxProfile); + if (pmaxp == nullptr) + { + swprintf(errMsg, errMsgLen, L"Error fetching maxp table"); + return false; + } + UnpackMaxp(pmaxp ,&this->profile); + + phead = (sfnt_FontHeader*)this->GetTablePointer(tag_FontHeader); + if (phead == nullptr) + { + swprintf(errMsg, errMsgLen, L"Unpacking head: error fetching table"); + return false; + } + head.version = SWAPL(phead->version); + head.fontRevision = SWAPL(phead->fontRevision); + head.checkSumAdjustment = SWAPL(phead->checkSumAdjustment); + head.magicNumber = SWAPL(phead->magicNumber); + head.flags = SWAPW(phead->flags); + head.unitsPerEm = SWAPW(phead->unitsPerEm); + //phead->created; + //phead->modified; + head.xMin = SWAPW(phead->xMin); + head.yMin = SWAPW(phead->yMin); + head.xMax = SWAPW(phead->xMax); + head.yMax = SWAPW(phead->yMax); + head.macStyle = SWAPW(phead->macStyle); + head.lowestRecPPEM = SWAPW(phead->lowestRecPPEM); + head.fontDirectionHint = SWAPW(phead->fontDirectionHint); + head.indexToLocFormat = SWAPW(phead->indexToLocFormat); + head.glyphDataFormat = SWAPW(phead->glyphDataFormat); + + this->unitsPerEm = head.unitsPerEm; + this->macStyle = head.macStyle; + this->metricProfile.xMin = head.xMin; + this->metricProfile.yMin = head.yMin; + this->metricProfile.xMax = head.xMax; + this->metricProfile.yMax = head.yMax; + if (head.unitsPerEm < 64 || head.unitsPerEm > 16384) { // this used to have an upper range of 17686 + swprintf(errMsg, errMsgLen, L"Unpacking head: em-Height %hi not in range 64 through 16384", head.unitsPerEm); + return false; + // according to Greg, unitsPerEm > 16384 would cause overflow in the rasterizer, plus imho the number 17686 doesn't make any sense to me. + } + + if (head.indexToLocFormat == SHORT_INDEX_TO_LOC_FORMAT) { + this->shortIndexToLocTable = true; + } else if (head.indexToLocFormat == LONG_INDEX_TO_LOC_FORMAT) { + this->shortIndexToLocTable = false; + } else { + swprintf(errMsg, errMsgLen, L"Unpacking head: Unknown indexToLocFormat %hi", head.indexToLocFormat); + return false; + } + this->outShortIndexToLocTable = this->shortIndexToLocTable; + + glitLen = this->GetTableLength(PRIVATE_GLIT1); +// it may be, though, that for very old fonts, created with TypeMan, we may run into trouble with the amateur version limitation with this code: +// these fonts may have a (historical) 2048 number for maxGlyphs simply because the glits were allocated for that size, even though the font's +// maxp has less than 256. + numGlyphs = this->NumberOfGlyphs(); + this->maxGlyphs = glitLen == 0 ? this->NumberOfGlyphs() : glitLen/sizeof(sfnt_FileDataEntry) - GLIT_PAD; // for the GLIT_PAD cf comment in UnpackGlitsLoca below + + this->AssertMaxGlyphs(this->maxGlyphs + GLIT_PAD); + + if (this->maxGlyphs + GLIT_PAD > this->maxGlitEntries) { + swprintf(errMsg, errMsgLen, L"This font has too many glyphs, please\r" BULLET L" increase the amount of virtual memory in your system settings"); + return false; + } + + phhea = (sfnt_HorizontalHeader*)this->GetTablePointer(tag_HoriHeader); + hhea.version = SWAPL(phhea->version); + hhea.yAscender= SWAPW(phhea->yAscender); + hhea.yDescender= SWAPW(phhea->yDescender); + hhea.yLineGap= SWAPW(phhea->yLineGap); + hhea.advanceWidthMax= SWAPW(phhea->advanceWidthMax); + hhea.minLeftSideBearing= SWAPW(phhea->minLeftSideBearing); + hhea.minRightSideBearing= SWAPW(phhea->minRightSideBearing); + hhea.xMaxExtent= SWAPW(phhea->xMaxExtent); + hhea.horizontalCaretSlopeNumerator= SWAPW(phhea->horizontalCaretSlopeNumerator); + hhea.horizontalCaretSlopeDenominator= SWAPW(phhea->horizontalCaretSlopeDenominator); + hhea.metricDataFormat= SWAPW(phhea->metricDataFormat); + hhea.numberOf_LongHorMetrics= SWAPW(phhea->numberOf_LongHorMetrics); + + this->metricProfile.advanceWidthMax = hhea.advanceWidthMax; + this->metricProfile.minLeftSideBearing = hhea.minLeftSideBearing; + this->metricProfile.minRightSideBearing = hhea.minRightSideBearing; + this->metricProfile.xMaxExtent = hhea.xMaxExtent; + + hmtx = (unsigned short *)this->GetTablePointer(tag_HorizontalMetrics); + k = 0; numEntries = hhea.numberOf_LongHorMetrics; + for (i = 0; i < numEntries; i++) { + aw = hmtx[k++]; + this->horMetric[i].advanceWidth = SWAPW(aw); + lsb = hmtx[k++]; + this->horMetric[i].leftSideBearing = SWAPW(lsb); + } + for (i = numEntries; i < numGlyphs; i++) { // all the remaining ones have the same aw of the last regular [aw,lsb] pair + this->horMetric[i].advanceWidth = SWAPW(aw); + lsb = hmtx[k++]; + this->horMetric[i].leftSideBearing = SWAPW(lsb); + } + + this->InitNewProfiles(); // silence BC errors + + // see comments in TrueTypeFont::UpdateAssemblerProfile + for (i = 0; i < Len(this->maxStackElements); i++) this->maxStackElements[i] = 0; + + return true; // by now +} // TrueTypeFont::UnpackHeadHheaMaxpHmtx + +bool TrueTypeFont::UnpackGlitsLoca(wchar_t errMsg[], size_t errMsgLen) { +/* the 'glit' tables are private tables to index the private tables that store the TMT and TT sources. + however, due to reasons that I don't remember, the (TT) sources for the prep and fpgm, as well as for + the cvt and the hinter parameters, are not stored in individual private tables, but as special glyphs + with glyph indeces 0xFFFA..0xFFFD (as per #defines at the top). on top of that, one of the glyph indeces + holds a magic number, which explains the ominous numberOfGlyph + GLIT_PAD below and the cascade of else ifs upon + unpacking the glit. + Additionnally, the length field on the file is only 16 bits, but the cvt source, which is one of the + glyph, may exceed this range. Therefore, internally the length is kept as 32 bits and computed from two + successive offsets, if the length is the "magic" number 0x8000, rather than taken from the file. In the + end, storing both the offsets (which are always 32 bits) *and* the lengths is redundant, anyhow, but + that is historical. + Finally, if the glits shouldn't contain enough entries, they are extended, by moving the last elements + up in the array. Escapes me, why this is done like this... */ + + int32_t tableLength,progrLength,i,j,numberOfGlyphs,oldMaxGlyph; + sfnt_FileDataEntry *fileGlit; + sfnt_MemDataEntry *glit1, *glit2; + uint32_t *longIndexToLoc; + unsigned short *shortIndexToLoc; + + numberOfGlyphs = this->NumberOfGlyphs(); + + this->glit1Entries = 0; + tableLength = this->GetTableLength(PRIVATE_GLIT1); + progrLength = this->GetTableLength(PRIVATE_PGM1); + if (tableLength > 0 /* && this->glit1Entries == 0 */) { + fileGlit = (sfnt_FileDataEntry*)this->GetTablePointer(PRIVATE_GLIT1); + this->glit1Entries = tableLength / sizeof(sfnt_FileDataEntry); + for (j = 0; j < this->glit1Entries; j++) { + this->glit1[j].glyphCode = SWAPW(fileGlit[j].glyphCode); + + if (j+6 == this->glit1Entries) { // last regular glyph, compute length but skip magic number (cf. below) + this->glit1[j].length = SWAPL(fileGlit[j+2].offset) - SWAPL(fileGlit[j].offset); + } else if (j+5 == this->glit1Entries) { // magic number, has no length + this->glit1[j].length = 0; + } else if (j+1 == this->glit1Entries) { // last special "glyph", compute length from table length + this->glit1[j].length = progrLength - SWAPL(fileGlit[j].offset); + } else if ((unsigned short)SWAPW(fileGlit[j].length) != (unsigned short)0x8000) { // length is short + // unless cast to unsigned short, compiler will sign extend the result of SWAPW to 32 bit signed, + // turning 0x8000 into 0ffff8000 and subsequently compare it to 0x00008000 !!! + this->glit1[j].length = SWAPW(fileGlit[j].length); + } else { // length is (regular case) long + this->glit1[j].length = SWAPL(fileGlit[j+1].offset) - SWAPL(fileGlit[j].offset); + } + this->glit1[j].offset = SWAPL(fileGlit[j].offset); + } + } + + this->glit2Entries = 0; + tableLength = this->GetTableLength(PRIVATE_GLIT2); + progrLength = this->GetTableLength(PRIVATE_PGM2); + if (tableLength > 0 /* && this->glit2Entries == 0 */) { + fileGlit = (sfnt_FileDataEntry*)this->GetTablePointer(PRIVATE_GLIT2); + this->glit2Entries = tableLength / sizeof(sfnt_FileDataEntry); + for (j = 0; j < this->glit2Entries; j++) { + this->glit2[j].glyphCode = SWAPW(fileGlit[j].glyphCode); + + if (j+6 == this->glit2Entries) { + this->glit2[j].length = SWAPL(fileGlit[j+2].offset) - SWAPL(fileGlit[j].offset); + } else if (j+5 == this->glit2Entries) { + this->glit2[j].length = 0; + } else if (j+1 == this->glit2Entries) { + this->glit2[j].length = progrLength - SWAPL(fileGlit[j].offset); + } else if ((unsigned short)SWAPW(fileGlit[j].length) != (unsigned short)0x8000) { + // unless cast to unsigned short, compiler will sign extend the result of SWAPW to 32 bit signed, + // turning 0x8000 into 0ffff8000 and subsequently compare it to 0x00008000 !!! + this->glit2[j].length = SWAPW(fileGlit[j].length); + } else { + this->glit2[j].length = SWAPL(fileGlit[j+1].offset) - SWAPL(fileGlit[j].offset); + } + this->glit2[j].offset = SWAPL(fileGlit[j].offset); + } + } + +// claudebe I know that there should be a better place to put this but I assume that +// we need to read again the loca table at the same time as we need to read the glit table +// this is an ugly programming under heavy time stress !!! + + longIndexToLoc = (uint32_t *)this->GetTablePointer(tag_IndexToLoc ); + shortIndexToLoc = (unsigned short *)longIndexToLoc; + this->numLocaEntries = this->GetTableLength(tag_IndexToLoc)/(this->shortIndexToLocTable ? sizeof(short) : sizeof(int32_t)) - 1; + for (i = 0; i <= this->numLocaEntries; i++) + this->IndexToLoc[i] = this->shortIndexToLocTable ? ((int32_t)((unsigned short)SWAPW(shortIndexToLoc[i]))) << 1 : SWAPL(longIndexToLoc[i]); + +// for (i = 0; i < Min(numberOfGlyphs,this->numLocaEntries); i++) // 2nd Ed Win98 fonts somehow got a loca table with one extra entry + for (i = 0; i < this->numLocaEntries; i++) // 2nd Ed Win98 fonts somehow got a loca table with one extra entry + if (this->IndexToLoc[i] > this->IndexToLoc[i+1]) { + swprintf(errMsg,errMsgLen,L"Unpacking loca: loca table not in ascending order %i %i %i",i, this->IndexToLoc[i], this->IndexToLoc[i + 1]); + return false; + } + + if (tableLength > 0 && (numberOfGlyphs + GLIT_PAD > this->glit1Entries || numberOfGlyphs + GLIT_PAD > this->glit2Entries)) { + oldMaxGlyph = this->glit1Entries - GLIT_PAD; + this->maxGlyphs = numberOfGlyphs; + glit1 = this->glit1; + glit2 = this->glit2; + for (i = this->maxGlyphs + 4,j = oldMaxGlyph + 4; i >= this->maxGlyphs ; i--, j-- ) { + glit1[i].glyphCode = glit1[j].glyphCode; + glit1[i].length = glit1[j].length; + glit1[i].offset = glit1[j].offset; + + glit2[i].glyphCode = glit2[j].glyphCode; + glit2[i].length = glit2[j].length; + glit2[i].offset = glit2[j].offset; + } + for (i = oldMaxGlyph; i < this->maxGlyphs ; i++ ) { + glit1[i].glyphCode = (short)i; + glit1[i].length = 0; + glit1[i].offset = 0; + + glit2[i].glyphCode = (short)i; + glit2[i].length = 0; + glit2[i].offset = 0; + } + this->glit1Entries = this->maxGlyphs + GLIT_PAD; + this->glit2Entries = this->maxGlyphs + GLIT_PAD; + } + return true; // by now +} // TrueTypeFont::UnpackGlitsLoca + +bool TrueTypeFont::UpdateMaxPointsAndContours(wchar_t errMsg[], size_t errMsgLen) { + // fonts like the Trebuchet have insufficient values for maxPoints, maxContours, maxCompositePoints, and maxCompositeContours, + // which creates a bootstrapping problem, given that TrueTypeFont::GetGlyph expects a correct font to be installed in the rasterizer. + // If this fails, we never get a chance to update the above profile values, hence we do it in here for prophylactic reasons... + + const wchar_t failureMsg[3][32] = {L"invalid 'maxp'", L"invalid 'glyf'", L"corrupt 'glyf'"}; + + int32_t glyphIndex,numGlyphs,numKnots,numContours,componentDepth,maxPoints,maxContours,maxCompositePoints,maxCompositeContours,failureCode; + unsigned char *maxpTable,*glyfTable; + + failureCode = 0; + maxpTable = this->GetTablePointer(tag_MaxProfile); + if (maxpTable == NULL) goto failure; + + failureCode = 1; + glyfTable = this->GetTablePointer(tag_GlyphData); + if (glyfTable == NULL) goto failure; + + failureCode = 2; + numGlyphs = this->numLocaEntries; + maxPoints = 0; maxCompositePoints = 0; + maxContours = 0; maxCompositeContours = 0; + for (glyphIndex = 0; glyphIndex < numGlyphs; glyphIndex++) { + numKnots = numContours = componentDepth = 0; + + if (!this->GetNumPointsAndContours(glyphIndex,&numKnots,&numContours,&componentDepth)) goto failure; + + if (componentDepth == 0) { // simple glyph + maxPoints = Max(maxPoints,numKnots); + maxContours = Max(maxContours,numContours); + } else { // composite glyph + maxCompositePoints = Max(maxCompositePoints,numKnots); + maxCompositeContours = Max(maxCompositeContours,numContours); + } + } + + UnpackMaxp(maxpTable,&this->profile); + this->profile.maxPoints = (uint16)maxPoints; + this->profile.maxContours = (uint16)maxContours; + this->profile.maxCompositePoints = (uint16)maxCompositePoints; + this->profile.maxCompositeContours = (uint16)maxCompositeContours; + PackMaxp(maxpTable,&this->profile); + + return true; +failure: + swprintf(errMsg,errMsgLen,L"Failed to update max points and contours due to " WIDE_STR_FORMAT L" table",failureMsg[failureCode]); + return false; +} // TrueTypeFont::UpdateMaxPointsAndContours + +void TrueTypeFont::EnterChar(int32_t glyphIndex, uint32_t charCode) { + UniGlyphMap entry; + entry.glyphIndex = (unsigned short) glyphIndex; + entry.unicode = charCode; + glyphIndexMap->insert(glyphIndexMap->end(),entry); + + this->charCodeOf[glyphIndex] = charCode; + this->numberOfChars++; +} // TrueTypeFont::EnterChar + +void TrueTypeFont::SortGlyphMap() { + std::sort(glyphIndexMap->begin(), glyphIndexMap->end(), Compare_UniGlyphMap); +} + +void TrueTypeFont::GetFmt0(sfnt_mappingTable *map) { // I made no attempt to understand this + int32_t i; + uint8 *glyphIdArray = (uint8 *)(&map[1]); + + for (i = 0; i < 256; i++) this->EnterChar(glyphIdArray[i],i); + + this->SortGlyphMap(); +} // TrueTypeFont::GetFmt0 + +typedef struct { + uint16 segCountX2; + uint16 searchRange; + uint16 entrySelector; + uint16 rangeShift; + uint16 endCount[1]; +} sfnt_cmap4hdr; + +void TrueTypeFont::GetFmt4(sfnt_mappingTable *map) { // I made no attempt to understand this + unsigned short j; + int16 i,segCount,*idDelta,delta; + uint16 gid, end, *endCount, start, *startCount, *idRangeOffset, offset, *glyphIdArray; + sfnt_cmap4hdr *cmap4hdr = (sfnt_cmap4hdr*)(&map[1]); + + segCount = SWAPW(cmap4hdr->segCountX2) >> 1; + endCount = (uint16 *)(&(cmap4hdr->endCount[0])); + startCount = endCount + (segCount + 1); + idDelta = (int16 *)&startCount[segCount]; + idRangeOffset = (uint16 *)&idDelta[segCount]; + glyphIdArray = &idRangeOffset[segCount]; + + + for (i = 0; i < segCount && endCount[i] != 0xFFFF; i++) { + start = SWAPW(startCount[i]); + end = SWAPW(endCount[i]); + delta = SWAPW(idDelta[i]); + offset = SWAPW(idRangeOffset[i]); + + if (0 == offset) { + for (j = start; j <= end; j++) this->EnterChar((uint16)(j + delta), j); + } else { + for (j = start; j <= end; j++) { + gid = idRangeOffset[offset/2 + j - start + i]; + this->EnterChar((uint16)SWAPW(gid), j); + } + } + } + this->SortGlyphMap(); +} // TrueTypeFont::GetFmt4 + +void TrueTypeFont::GetFmt6(sfnt_mappingTable *map) { // I made no attempt to understand this + int32_t i,entries,firstCode,glyphIndex; + sfnt_mappingTable6 *Fmt6Table = (sfnt_mappingTable6 *)(reinterpret_cast(map) + sizeof(sfnt_mappingTable6)); + + firstCode = (int32_t)SWAPW(Fmt6Table->firstCode); + entries = (int32_t)SWAPW(Fmt6Table->entryCount); + for (i = 0; i < entries; i++) { + glyphIndex = SWAPW(Fmt6Table->glyphIdArray[i]); + this->EnterChar(glyphIndex,firstCode + i); + } + this->SortGlyphMap(); +} // TrueTypeFont::GetFmt6 + +void TrueTypeFont::GetFmt12(sfnt_mappingTable *map) +{ + sfnt_mappingTable12 *fntTable = reinterpret_cast(map); + sfnt_mappingTable12Record *record; + uint32 i, j, count, nGroups = SWAPL(fntTable->nGroups); + + for (i = 0; i < nGroups; i++) { + record = reinterpret_cast(reinterpret_cast(map) + sizeof(sfnt_mappingTable12) + i * sizeof(sfnt_mappingTable12Record)); + uint32 startCharCode = SWAPL(record->startCharCode); + uint32 endCharCode = SWAPL(record->endCharCode); + uint32 startGlyphID = SWAPL(record->startGlyphCode); + for ( j = startCharCode, count = 0; j <= endCharCode; j++, count++) + this->EnterChar(startGlyphID + count, j); + } + this->SortGlyphMap(); +} + +bool TrueTypeFont::UnpackCharGroup(wchar_t errMsg[], size_t errMsgLen) { + int32_t i,numberOfGlyph; + short *charGroup,tempGroup; + + for (i = 0; i < this->maxGlitEntries; i++) this->charGroupOf[i] = otherCase; + + if (this->TableExists(PRIVATE_GROUP)) { + numberOfGlyph = this->GetTableLength(PRIVATE_GROUP) >> 1; + if (numberOfGlyph <= this->maxGlitEntries) { + charGroup = (short *)this->GetTablePointer(PRIVATE_GROUP); + + for (i = 0; i < numberOfGlyph; i++) { + tempGroup = SWAPW(charGroup[i]); + if (tempGroup < TOTALTYPE) tempGroup = intInFileToCharGroup[tempGroup]; // translate old char groups + this->charGroupOf[i] = (unsigned char)tempGroup; + } + + } + } + return true; // by now +} // TrueTypeFont::UnpackCharGroup + +bool TrueTypeFont::GetSource(bool lowLevel, int32_t glyphIndex, TextBuffer *source, wchar_t errMsg[], size_t errMsgLen) { + sfnt_MemDataEntry *glit; + int32_t len,glitEntries,glitIndex,pgmID; + + if (lowLevel) { + pgmID = PRIVATE_PGM1; glit = this->glit1; glitEntries = this->glit1Entries; + } else { + pgmID = PRIVATE_PGM2; glit = this->glit2; glitEntries = this->glit2Entries; + } + + for (glitIndex = 0; glitIndex < glitEntries && glit[glitIndex].glyphCode != glyphIndex; glitIndex++); + len = this->GetTableLength(pgmID); + if (len > 0 && glitIndex < glitEntries && glit[this->maxGlyphs].offset == PRIVATE_STAMP_1) + { + source->SetText(glit[glitIndex].length,(char*)(this->GetTablePointer(pgmID) + glit[glitIndex].offset)); + } + else + { + swprintf(errMsg, errMsgLen, L"Unpacking source: "); + if (len == 0) + swprintf(&errMsg[STRLENW(errMsg)], errMsgLen, L"private " WIDE_STR_FORMAT L"level table empty", lowLevel ? L"low" : L"high"); + else if (glitIndex == glitEntries) + swprintf(&errMsg[STRLENW(errMsg)], errMsgLen, L"glyph %li not in private glit", glyphIndex); + else + swprintf(&errMsg[STRLENW(errMsg)], errMsgLen, L"bad private stamp 1"); + source->SetText(0,(wchar_t*)NULL); + return len == 0; // ok to have no sources + } + return true; // by now +} // TrueTypeFont::GetSource + +bool TrueTypeFont::GetTTOTable(sfnt_TableTag srcTag, TextBuffer *src, sfnt_TableTag binTag, ASMType asmType) { + src->SetText(this->GetTableLength(srcTag),(char *)this->GetTablePointer(srcTag)); + return this->UpdateBinData(asmType,this->GetTableLength(binTag),this->GetTablePointer(binTag)); +} // TrueTypeFont::GetTTOTable + + +/* fix this */ // huh? I don't claim to understand all of this in BuildNewSfnt & sqq... +#define variableTables 16 + +class TableAvailability +{ +public: + TableAvailability() : tag_(0), required_(false), strip_(stripNothing) {} + TableAvailability(sfnt_TableTag tag, bool required, StripCommand strip) : tag_(tag), required_(required), strip_(strip) {} + + sfnt_TableTag Tag() const + { + return tag_; + } + + bool Required() const + { + return required_; + } + + StripCommand Strip() const + { + return strip_; + } + +private: + sfnt_TableTag tag_; + bool required_; // to work with VTT + StripCommand strip_; // i.e. strip if incoming strip >= this value for strip +}; + +void SwapEntries(sfnt_DirectoryEntry tbl[], int32_t *a, int32_t *b) { + sfnt_DirectoryEntry entry; + int32_t i; + + entry = tbl[*a]; tbl[*a] = tbl[*b]; tbl[*b] = entry; + i = *a; *a = *b; *b = i; +} // SwapEntries + +void FillNewGlit(sfnt_MemDataEntry glit[], int32_t maxGlyphs, int32_t glitEntries) { + int32_t i,j; + + for (i = 0; i < glitEntries; i++) { + glit[i].glyphCode = (unsigned short)i; + glit[i].length = 0; + glit[i].offset = 0; + } + glit[maxGlyphs].offset = PRIVATE_STAMP_1; // Stamp it + for (i = maxGlyphs, j = 0; j < GLIT_PAD; i++, j++) glit[i].glyphCode = glitPadIndex[j]; +} // FillNewGlit + +uint32_t PackGlit(sfnt_FileDataEntry *fileGlit, int32_t glitEntries, sfnt_MemDataEntry *memGlit) { + int32_t i,length; + + for (i = 0; i < glitEntries; i++) { + fileGlit[i].glyphCode = SWAPW(memGlit[i].glyphCode); + + length = memGlit[i].length > 0x08000 ? 0x08000 : (unsigned short)memGlit[i].length; + fileGlit[i].length = SWAPW(length); + fileGlit[i].offset = SWAPL(memGlit[i].offset); + } + + return glitEntries*sizeof(sfnt_FileDataEntry); +} // PackGlit + +bool BuildOffsetTables(unsigned char *sfnt, uint32_t maxSfntSize, sfnt_OffsetTable *offsetTable, sfnt_OffsetTable *tmpOffsetTable, int32_t numVariableTables, TableAvailability avail[], StripCommand strip, int32_t *headerSize) { + int32_t i,j,j2,maxPowerOf2,headIdx,glyfIdx,locaIdx,hheaIdx,hmtxIdx,tag; + sfnt_DirectoryEntry entry = {(sfnt_TableTag)0x3F3F3F3F /* '????' */, 0, 0, 0}; // tag, checksum, offset, length + +// first, do some sanity check on the offset table, to be done upon reading a font? +// another inherited hack: a flat MYOFFSETTABLESIZE bytes are allocated for the internal copy of the sfnt directory +// so first assert that this is enough to hold all directory entries (see sfnt.h for type definitions) +// to do so, very first thing is to get the numOffsets + memcpy((char*)offsetTable,(char*)sfnt,OFFSETTABLESIZE); + offsetTable->numOffsets = SWAPW(offsetTable->numOffsets); + assert(sizeof(sfnt_OffsetTable) + (offsetTable->numOffsets - 1)*sizeof(sfnt_DirectoryEntry) <= MYOFFSETTABLESIZE); +// now get the rest + memcpy((char*)&offsetTable->table[0],(char*)&sfnt[OFFSETTABLESIZE],MYOFFSETTABLESIZE-OFFSETTABLESIZE); + + for (i = 0; i < offsetTable->numOffsets; i++) { + if (offsetTable->table[i].length != 0) { + offsetTable->table[i].offset = SWAPL(offsetTable->table[i].offset); + offsetTable->table[i].length = SWAPL(offsetTable->table[i].length); + if (offsetTable->table[i].offset > maxSfntSize) return false; + } else { + offsetTable->table[i].offset = 0; + } + offsetTable->table[i].tag = SWAPL(offsetTable->table[i].tag); + offsetTable->table[i].checkSum = SWAPL(offsetTable->table[i].checkSum); + } + +// now build a temporary offset table containing the tables to be used in the sfnt to be built + memcpy((char*)tmpOffsetTable,(char*)offsetTable, MYOFFSETTABLESIZE); + for (i = 0, j2 = tmpOffsetTable->numOffsets; i < numVariableTables; i++) { + // all tables that we need + for (j = 0; j < tmpOffsetTable->numOffsets && tmpOffsetTable->table[j].tag != avail[i].Tag(); j++) + ; + if (avail[i].Required() && j == tmpOffsetTable->numOffsets) { // required but not present + entry.tag = avail[i].Tag(); + tmpOffsetTable->table[j2++] = entry; // add + } + } + tmpOffsetTable->numOffsets = (short)j2; + for (j = 0, j2 = 0; j < tmpOffsetTable->numOffsets; j++) { + // all tables that we have already + for (i = 0; i < numVariableTables && tmpOffsetTable->table[j].tag != avail[i].Tag(); i++) + ; + if (i == numVariableTables || strip < avail[i].Strip()) { + // not strippable at all or not high enough stripping priviledges + tmpOffsetTable->table[j2++] = tmpOffsetTable->table[j]; // keep + } + } + tmpOffsetTable->numOffsets = (short)j2; + +// finish temporary offset table + tmpOffsetTable->version = 0x00010000; + maxPowerOf2 = 2; + for (tmpOffsetTable->entrySelector = 0; maxPowerOf2 <= tmpOffsetTable->numOffsets; tmpOffsetTable->entrySelector++) maxPowerOf2 <<= 1; + maxPowerOf2 >>= 1; + tmpOffsetTable->searchRange = (uint16)(maxPowerOf2 * 16); + tmpOffsetTable->rangeShift = tmpOffsetTable->numOffsets * 16 - tmpOffsetTable->searchRange; + +// remainder of sfnt starts at offset 'headerSize' + *headerSize = OFFSETTABLESIZE + tmpOffsetTable->numOffsets*sizeof(sfnt_DirectoryEntry); // Header + +// make sure we get to do 'head' and 'glyf' before 'loca' because 'loca' depends on them; +// likewise, 'hmtx' depends on 'hhea' (numberOfHMetrics), which depends on 'glyf' (metrics, useMyMetrics) + headIdx = glyfIdx = locaIdx = hheaIdx = hmtxIdx = 0; + for (i = 0; i < tmpOffsetTable->numOffsets; i++) { + tag = tmpOffsetTable->table[i].tag; + if (tag == tag_FontHeader) headIdx = i; + else if (tag == tag_GlyphData) glyfIdx = i; + else if (tag == tag_IndexToLoc) locaIdx = i; + else if (tag == tag_HoriHeader) hheaIdx = i; + else if (tag == tag_HorizontalMetrics) hmtxIdx = i; + } + if (glyfIdx > hheaIdx) SwapEntries(tmpOffsetTable->table,&glyfIdx,&hheaIdx); + if (hheaIdx > hmtxIdx) SwapEntries(tmpOffsetTable->table,&hheaIdx,&hmtxIdx); + if (headIdx > locaIdx) SwapEntries(tmpOffsetTable->table,&headIdx,&locaIdx); + if (glyfIdx > locaIdx) SwapEntries(tmpOffsetTable->table,&glyfIdx,&locaIdx); + + return true; // by now +} // BuildOffsetTables + +bool TrueTypeFont::BuildNewSfnt(StripCommand strip, CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, + TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + wchar_t errMsg[], size_t errMsgLen) { + unsigned char *sfnt,*tmpSfnt,*tmpSfntHandle; + + int32_t headerSize,i,j,tag,numberOfGlyphs,numberOfHMetrics = 0,zero = 0L,pad; + uint32_t sfntPos = 0,tmpSfntSize,sizeOfTable,newSfntSizeEstimate; + sfnt_FontHeader *head; + sfnt_HorizontalHeader *hhea; + bool result = false; + + // fileGlit will point into tmpSfnt as it is being built (tags PRIVATE_GLIT1 and PRIVATE_GLIT2) + // its correct setup is relied on upon packing PRIVATE_PGM1 and PRIVATE_PGM2 (not BeatS design...) + sfnt_MemDataEntry *memGlit = NULL; + sfnt_FileDataEntry *fileGlit = NULL; + + // cannot have variables in initialization of array, hence... + TableAvailability avail[variableTables]; + + avail[ 0] = TableAvailability(tag_ControlValue, this->cvt->HighestCvtNum() >= 0, stripHints); // 0 + avail[ 1] = TableAvailability(tag_FontProgram, this->binSize[asmFPGM] > 0, stripHints); // 1 + avail[ 2] = TableAvailability(tag_HoriDeviceMetrics, this->hdmxBinSize > 0, stripBinary); // 2 + avail[ 3] = TableAvailability(tag_LinearThreshold, this->ltshBinSize > 0, stripBinary); // 3 + avail[ 4] = TableAvailability(tag_VertDeviceMetrics, this->vdmxBinSize > 0, stripBinary); // 4 + avail[ 5] = TableAvailability(tag_GridfitAndScanProc, this->gaspBinSize > 0, stripEverything); // 5 + avail[ 6] = TableAvailability(tag_PreProgram, this->binSize[asmPREP] > 0, stripHints); // 6 + avail[ 7] = TableAvailability(tag_NamingTable, true, stripEverything); // 7 + avail[ 8] = TableAvailability(PRIVATE_GLIT1, true, stripSource); // 8 + avail[ 9] = TableAvailability(PRIVATE_PGM1, true, stripSource); // 9 + avail[10] = TableAvailability(PRIVATE_GLIT2, true, stripSource); // 10 + avail[11] = TableAvailability(PRIVATE_PGM2, true, stripSource); // 11 + avail[12] = TableAvailability(PRIVATE_GROUP, true, stripSource); // 12 + avail[13] = TableAvailability(PRIVATE_CMAP, false, stripNothing); // 13 + avail[14] = TableAvailability(tag_CVTVariations, this->IsCvarTupleData(), stripHints); // 14 + avail[15] = TableAvailability(PRIVATE_CVAR, this->IsCvarTupleData(), stripSource); // 15 + + if (strip >= stripSource) { + cvtText-> Delete(0,cvtText-> TheLength()); + fpgmText->Delete(0,fpgmText->TheLength()); + prepText->Delete(0,prepText->TheLength()); + glyfText->Delete(0,glyfText->TheLength()); + talkText->Delete(0,talkText->TheLength()); + } + + head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); + if (SWAPL(head->magicNumber) != SFNT_MAGIC) { + swprintf(errMsg, errMsgLen, L"BuildNewSfnt: Bad magic number in the head"); + goto term; + } + + if (glyphIndex < 0 || this->profile.numGlyphs <= glyphIndex) { + swprintf(errMsg, errMsgLen, L"BuildNewSfnt: invalid glyph index %li of current glyph", glyphIndex); + goto term; + } + + if (strip >= stripHints) { + //for (i = 0; i < numASMTypes; i++) this->UpdateBinData((ASMType)i,0,NULL); + for (i = firstTTASMType; i <= lastTTASMType; i++) this->UpdateBinData((ASMType)i,0,NULL); + this->cvt->PutCvtBinary(0,NULL); // in hopes that copying 0 bytes from address NULL doesn't cause a bus-error... + } + +// numberOfGlyphs = this->NumberOfGlyphs(); +// we have to use profile.numGlyphs here because the font may have glyphs added to it while the loca table has not yet been updated and NumberOfGlyphs() uses the loca table... + numberOfGlyphs = this->profile.numGlyphs; + + this->UpdateGlyphProfile(glyph); + this->PackMaxpHeadHhea(); + + sfnt = this->sfntHandle; // alias it + + if (!BuildOffsetTables(sfnt,this->sfntSize,this->offsetTable,this->tmpOffsetTable,variableTables,avail,strip,&headerSize)) { + swprintf(errMsg, errMsgLen, L"BuildNewSfnt: offset is too large"); // claudebe, problem with a new font received from Sampo + goto term; + } + +#ifdef _DEBUG +// given MYOFFSETTABLESIZE for the size of the entire sfnt_OffsetTable, maxNumDirectoryEntries denotes +// the (truncated) number of sfnt_DirectoryEntry entries that this amount of memory is good for +// (see comments about inherited hack above) +#define maxNumDirectoryEntries ((MYOFFSETTABLESIZE - sizeof(sfnt_OffsetTable))/sizeof(sfnt_DirectoryEntry) + 1) + uint32_t sizeEstimate[maxNumDirectoryEntries]; + char tagCopy[maxNumDirectoryEntries][8]; + for (i = 0; i < maxNumDirectoryEntries; i++) { sizeEstimate[i] = 0; tagCopy[i][0] = 0; } +#endif + + // 1. pass: get a reasonnable upper limit for the expected file size + newSfntSizeEstimate = headerSize; + + for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { + + switch (tag = this->tmpOffsetTable->table[i].tag) { + + case tag_FontHeader: + sizeOfTable = this->tmpOffsetTable->table[i].length; + break; + case tag_HoriHeader: { + sizeOfTable = this->tmpOffsetTable->table[i].length; + break; + } + case tag_HorizontalMetrics: + // discovered in an old version of Courier New Italic, glyph "E" had a different AW than a composite using "E" (and E's metrics) + // which was correctly propagated to the hmtx upon PackGlyph, after which point the actual packing routine for the hmtx worked + // with a different numberOfHMetrics triggering an assert because a differing table size was detected + // Therefore, for size estimate always assume each glyph as a different AW + sizeOfTable = numberOfGlyphs*sizeof(sfnt_HorizontalMetrics); + break; + case tag_GlyphData: + sizeOfTable = this->GetPackedGlyphsSizeEstimate(glyph,glyphIndex,this->IndexToLoc); + break; + case tag_IndexToLoc: + // for size estimate, always assume long format 'loca' table; correct this->outShortIndexToLocTable determined in PackGlyph only + sizeOfTable = (numberOfGlyphs + 1)*sizeof(int32_t); + break; + case PRIVATE_GROUP: + sizeOfTable = numberOfGlyphs*sizeof(short); + break; + case PRIVATE_GLIT1: + case PRIVATE_GLIT2: { + int32_t glitEntries; + bool newGlit; + + newGlit = this->tmpOffsetTable->table[i].length == 0 || this->tmpOffsetTable->table[i].offset == 0; // we've probably just added the table + + if (!newGlit) + glitEntries = tag == PRIVATE_GLIT1 ? this->glit1Entries : this->glit2Entries; + else { + glitEntries = this->maxGlyphs + GLIT_PAD; + memGlit = tag == PRIVATE_GLIT1 ? this->glit1 : this->glit2; + FillNewGlit(memGlit,this->maxGlyphs,glitEntries); + } + + sizeOfTable = glitEntries*sizeof(sfnt_FileDataEntry); + break; + } + case PRIVATE_CVAR: + sizeOfTable = this->tsicBinSize = this->EstimatePrivateCvar(); + break; + case PRIVATE_PGM1: + case PRIVATE_PGM2: + sizeOfTable = GetPackedGlyphSourcesSize(glyfText,prepText,cvtText,talkText,fpgmText, + tag == PRIVATE_PGM1 ? 1 : 2,glyphIndex,tag == PRIVATE_PGM1 ? this->glit1 : this->glit2); + break; + case tag_PreProgram: + sizeOfTable = this->binSize[asmPREP]; + break; + case tag_FontProgram: + sizeOfTable = this->binSize[asmFPGM]; + break; + case tag_ControlValue: + sizeOfTable = this->cvt->GetCvtBinarySize(); + break; + case tag_GridfitAndScanProc: + sizeOfTable = this->gaspBinSize ? this->gaspBinSize : this->tmpOffsetTable->table[i].length; + break; + case tag_CVTVariations: + sizeOfTable = this->cvarBinSize = this->EstimateCvar(); + break; + case tag_NamingTable: + sizeOfTable = this->tmpOffsetTable->table[i].length == 0 ? 6 : this->tmpOffsetTable->table[i].length; + break; + default: + sizeOfTable = this->tmpOffsetTable->table[i].length; + break; + } // switch (tag) + +#ifdef _DEBUG + sizeEstimate[i] = sizeOfTable; + + tagCopy[i][0] = (char)(tag >> 24); + tagCopy[i][1] = (char)(tag >> 16); + tagCopy[i][2] = (char)(tag >> 8); + tagCopy[i][3] = (char)(tag); + tagCopy[i][4] = (char)0; +#endif + + sizeOfTable = DWordPad(sizeOfTable); + newSfntSizeEstimate += sizeOfTable; + } + + this->AssertMaxSfntSize(newSfntSizeEstimate,false,true); // make sure we have a large enough tmpSfnt + + if (newSfntSizeEstimate > this->maxTmpSfntSize) { + MaxSfntSizeError(L"BuildNewSfnt: This font is getting too large",newSfntSizeEstimate,errMsg,errMsgLen); goto term; + } + + // 2. pass: build the new sfnt into the tmpSfntHandle + + tmpSfnt = this->sfntTmpHandle; // alias this one, too + + sfntPos = headerSize; // skip header, need to fill directory first, then plug in header as final step + + for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { + + switch (tag = this->tmpOffsetTable->table[i].tag) { + + case tag_FontHeader: { +#ifdef _DEBUG + sizeOfTable = this->tmpOffsetTable->table[i].length; +#endif + memcpy((char*) &tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); + head = (sfnt_FontHeader *)&tmpSfnt[sfntPos]; + long long temp_time = DateTime(); // ADD GREGH 20101209 + int32_t temp_lower = (int32_t)(temp_time & 0xFFFFFFFF); // ADD GREGH 20101209 + int32_t temp_upper = (int32_t)((temp_time >> 32) & 0xFFFFFFFF); // ADD GREGH 20101209 + head->modified.bc = SWAPL(temp_upper); // ADD GREGH 20101209 + head->modified.ad = SWAPL(temp_lower); // ADD GREGH 20101209 +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + } + case tag_HoriHeader: { // determine numberOfHMetrics, required in tag_HorizontalMetrics below!!! + int32_t aw; +#ifdef _DEBUG + sizeOfTable = this->tmpOffsetTable->table[i].length; +#endif + memcpy((char*) &tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); + + aw = this->horMetric[numberOfGlyphs-1].advanceWidth; + for (numberOfHMetrics = numberOfGlyphs; numberOfHMetrics >= 2 && this->horMetric[numberOfHMetrics-2].advanceWidth == aw; numberOfHMetrics--); + + hhea = (sfnt_HorizontalHeader *)&tmpSfnt[sfntPos]; + hhea->numberOf_LongHorMetrics = CSWAPW((unsigned short)numberOfHMetrics); +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + + } + case tag_HorizontalMetrics: { + int32_t s,d; + unsigned short *hmtx; +#ifdef _DEBUG + // for size estimate always assume each glyph as a different AW + sizeOfTable = numberOfGlyphs*sizeof(sfnt_HorizontalMetrics); +#endif + hmtx = (unsigned short *)&tmpSfnt[sfntPos]; + s = d = 0; + while (s < numberOfHMetrics) { + hmtx[d++] = SWAPW(this->horMetric[s].advanceWidth); + hmtx[d++] = SWAPW(this->horMetric[s].leftSideBearing); + s++; + } + while (s < numberOfGlyphs) { + // remainder of hmtx consists of lsb's only + hmtx[d++] = SWAPW(this->horMetric[s].leftSideBearing); + s++; + } + + this->tmpOffsetTable->table[i].length = d*sizeof(short); +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length <= sizeOfTable); +#endif + break; + } + + case tag_GlyphData: +#ifdef _DEBUG + sizeOfTable = this->GetPackedGlyphsSizeEstimate(glyph,glyphIndex,this->IndexToLoc); +#endif + memcpy((char*)this->tmpIndexToLoc,(char*)this->IndexToLoc, (numberOfGlyphs + 1) * sizeof(int32_t)); + + this->tmpOffsetTable->table[i].length = this->PackGlyphs(strip,glyph,glyphIndex,this->IndexToLoc,this->tmpIndexToLoc,&tmpSfnt[sfntPos]); +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length <= sizeOfTable); +#endif + break; + + case tag_IndexToLoc: + // here 'head' is (still) valid and pointing to the header of the sfnt being built + + // for size estimate, always assume long format 'loca' table; correct this->outShortIndexToLocTable determined in PackGlyph only +#ifdef _DEBUG + sizeOfTable = (numberOfGlyphs + 1)*sizeof(int32_t); +#endif + if (this->outShortIndexToLocTable) { + unsigned short *shortIndexToLoc = (unsigned short *)&tmpSfnt[sfntPos]; + + this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(short); + head->indexToLocFormat = CSWAPW(SHORT_INDEX_TO_LOC_FORMAT); + for (j = 0; j <= numberOfGlyphs; j++) + { + unsigned short tempIdx = (unsigned short)(this->tmpIndexToLoc[j] >> 1); + shortIndexToLoc[j] = SWAPW(tempIdx); + } + } else { + int32_t *longIndexLoca = (int32_t *)&tmpSfnt[sfntPos]; + + this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(int32_t); + head->indexToLocFormat = CSWAPW(LONG_INDEX_TO_LOC_FORMAT); + for (j = 0; j <= numberOfGlyphs; j++) + { + longIndexLoca[j] = SWAPL(this->tmpIndexToLoc[j]); + } + } +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length <= sizeOfTable); +#endif + break; + + case PRIVATE_GROUP: { + int32_t j; + short *charGroup,tempGroup; + +#ifdef _DEBUG + sizeOfTable = numberOfGlyphs*sizeof(short); +#endif + this->tmpOffsetTable->table[i].length = numberOfGlyphs * sizeof(short); + charGroup = (short *)&tmpSfnt[sfntPos]; + this->charGroupOf[glyphIndex] = group; + for (j = 0; j < numberOfGlyphs; j++) { + tempGroup = this->charGroupOf[j]; + if (tempGroup < TOTALTYPE) tempGroup = charGroupToIntInFile[tempGroup]; // translate old char groups + charGroup[j] = SWAPW(tempGroup); + } +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + } + + case PRIVATE_GLIT1: + case PRIVATE_GLIT2: { + int32_t glitEntries; + bool newGlit; + + newGlit = this->tmpOffsetTable->table[i].length == 0 || this->tmpOffsetTable->table[i].offset == 0; // we've probably just added the table + + if (tag == PRIVATE_GLIT1) { + memGlit = this->glit1; + glitEntries = this->glit1Entries; + } else { // tag == PRIVATE_GLIT2 + memGlit = this->glit2; + glitEntries = this->glit2Entries; + } + if (newGlit) { + glitEntries = this->maxGlyphs + GLIT_PAD; + FillNewGlit(memGlit,this->maxGlyphs,glitEntries); + } +#ifdef _DEBUG + sizeOfTable = glitEntries*sizeof(sfnt_FileDataEntry); +#endif + fileGlit = (sfnt_FileDataEntry *)&tmpSfnt[sfntPos]; // need to assign for case PRIVATE_PGM right below (not BeatS architecture...) + this->tmpOffsetTable->table[i].length = PackGlit(fileGlit,glitEntries,memGlit); + +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + } + + case PRIVATE_PGM1: + case PRIVATE_PGM2: { + // this assumes that we do the PRIVATE_GLIT1 and 2 prior to the respective PRIVATE_PGM1 and 2, + // which is a valid assumption as long as VTT is the only instance to add and remove either of them, + // otherwise the fileGlit and memGlit below are not pointing to the correct locations in the sfnt being built +#ifdef _DEBUG + sizeOfTable = GetPackedGlyphSourcesSize(glyfText,prepText,cvtText,talkText,fpgmText, + tag == PRIVATE_PGM1 ? 1 : 2,glyphIndex,memGlit); +#endif + this->PackGlyphSources(glyfText,prepText,cvtText,talkText,fpgmText,tag == PRIVATE_PGM1 ? 1 : 2,glyphIndex, + fileGlit,memGlit,(uint32_t*)(&this->tmpOffsetTable->table[i].length),&tmpSfnt[sfntPos]); +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + + } + + case PRIVATE_CVAR: + sizeOfTable = this->tsicBinSize; + this->UpdatePrivateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); + break; + + case tag_PreProgram: +#ifdef _DEBUG + sizeOfTable = this->binSize[asmPREP]; +#endif + memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmPREP],this->binSize[asmPREP]); + this->tmpOffsetTable->table[i].length = this->binSize[asmPREP]; +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + case tag_FontProgram: +#ifdef _DEBUG + sizeOfTable = this->binSize[asmFPGM]; +#endif + memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmFPGM],this->binSize[asmFPGM]); + this->tmpOffsetTable->table[i].length = this->binSize[asmFPGM]; +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + + case tag_ControlValue: +#ifdef _DEBUG + sizeOfTable = this->cvt->GetCvtBinarySize(); +#endif + this->cvt->GetCvtBinary((int32_t*)&this->tmpOffsetTable->table[i].length,&tmpSfnt[sfntPos]); +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + + // case tag_HoriDeviceMetrics: not handled in VTT anymore... + // case tag_LinearThreshold: not handled in VTT anymore... + // case tag_VertDeviceMetrics: not handled in VTT anymore... + + case tag_CVTVariations: + sizeOfTable = this->cvarBinSize; + this->UpdateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); + break; + + case tag_GridfitAndScanProc: + if (this->gaspBinSize) { + + unsigned short *packed; + int32_t s; +#ifdef _DEBUG + sizeOfTable = this->gaspBinSize; +#endif + this->tmpOffsetTable->table[i].length = this->gaspBinSize; + packed = (unsigned short *)&tmpSfnt[sfntPos]; + *packed++ = SWAPW(this->gaspTable.version); + *packed++ = SWAPW(this->gaspTable.numRanges); + for (s = 0; s < this->gaspTable.numRanges; s++) { + unsigned short rangeMaxPPEM = this->gaspTable.gaspRange.at(s).rangeMaxPPEM; + unsigned short rangeGaspBehavior = this->gaspTable.gaspRange.at(s).rangeGaspBehavior; + *packed++ = SWAPW(rangeMaxPPEM); + *packed++ = SWAPW(rangeGaspBehavior); + } +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + } else { + // if this->gaspBinSize = 0 then VTT hasn't changed gasp table, so copy from original + goto copyOld; + } + break; + + case tag_NamingTable: + if (this->tmpOffsetTable->table[i].length == 0) { // we've just added a name table; does this make sense at all? we're not editing name tables in here (anymore) +#ifdef _DEBUG + sizeOfTable = 6; +#endif + this->tmpOffsetTable->table[i].length = 6; + memcpy((char*)&tmpSfnt[sfntPos], "\0\0\0\0\0\6", 6L); // dummy name table +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + } else { + goto copyOld; + } + + break; + + default: + copyOld: +#ifdef _DEBUG + sizeOfTable = this->tmpOffsetTable->table[i].length; +#endif + memcpy((char*)&tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); + +#ifdef _DEBUG + assert(this->tmpOffsetTable->table[i].length == sizeOfTable); +#endif + break; + } // switch (tag) +#ifdef _DEBUG + assert(sizeEstimate[i] == sizeOfTable && sizeEstimate[i] >= this->tmpOffsetTable->table[i].length); +#endif + this->tmpOffsetTable->table[i].offset = sfntPos; + sfntPos += this->tmpOffsetTable->table[i].length; + + pad = DWordPad(sfntPos) - sfntPos; + memcpy((char*)&tmpSfnt[sfntPos],(char*)&zero,pad); // zero pad for long word alignment + sfntPos += pad; + } +#ifdef _DEBUG + assert(newSfntSizeEstimate >= sfntPos); +#endif +// once we've finished plugging in all the tables, plug in the directory (temporary offset table) + + { sfnt_DirectoryEntry *entry; + + entry = &(this->tmpOffsetTable->table[0]); + for (i=0; itmpOffsetTable->numOffsets; i++, entry++) + { + entry->tag = SWAPL(entry->tag); + entry->checkSum = SWAPL(entry->checkSum); + entry->offset = SWAPL(entry->offset); + entry->length = SWAPL(entry->length); + } + this->tmpOffsetTable->version = SWAPL(this->tmpOffsetTable->version); + this->tmpOffsetTable->numOffsets = SWAPW(this->tmpOffsetTable->numOffsets); + this->tmpOffsetTable->searchRange = SWAPW(this->tmpOffsetTable->searchRange); + this->tmpOffsetTable->entrySelector = SWAPW(this->tmpOffsetTable->entrySelector); + this->tmpOffsetTable->rangeShift = SWAPW(this->tmpOffsetTable->rangeShift); + } + + memcpy((char*)tmpSfnt,(char*)this->tmpOffsetTable, headerSize); + + result = true; // by now + +term: // function termination code + if (result) { // successful so far, now swap sfntTmpHandle <-> sfntHandle and install new sfnt; swap => no resize/handle deallocation/handle copy/memory fragmentation + tmpSfntHandle = this->sfntHandle; this->sfntHandle = this->sfntTmpHandle; this->sfntTmpHandle = tmpSfntHandle; + tmpSfntSize = this->maxSfntSize; this->maxSfntSize = this->maxTmpSfntSize; this->maxTmpSfntSize = tmpSfntSize; + this->sfntSize = sfntPos; + + this->CalculateNewCheckSums(); + this->CalculateCheckSumAdjustment(); + this->SortTableDirectory(); + + // claudebe the glit entries must be updated + this->glit1Entries = this->glit2Entries = 0; + this->shortIndexToLocTable = this->outShortIndexToLocTable; + + result = this->UnpackGlitsLoca(errMsg,errMsgLen) && this->UpdateMaxPointsAndContours(errMsg,errMsgLen) && this->UnpackCharGroup(errMsg,errMsgLen) && this->SetSfnt( -1, -1, errMsg,errMsgLen); + } + return result; +} // TrueTypeFont::BuildNewSfnt + +#define maxNumGlyphs 0x10000 // 64k +#define minDynamicArraySize 0x100000 // 1 MB + +typedef struct { + int32_t size,used; + int32_t dataPos[maxNumGlyphs]; + unsigned char *data; +} GlyphTableArray; + +bool InitDynamicArray(GlyphTableArray *dyn) { + dyn->data = (unsigned char *)NewP(minDynamicArraySize); + dyn->size = dyn->data != NULL ? minDynamicArraySize : 0; + dyn->used = 0; + return dyn->data != NULL; +} // InitDynamicArray + +bool AssertDynamicArraySize(GlyphTableArray *dyn, int32_t deltaSize) { + int32_t newSize; + unsigned char *tmpData; + + if (dyn->used + deltaSize <= dyn->size) return true; + + newSize = ((AddReserve(dyn->size + deltaSize) + minDynamicArraySize - 1)/minDynamicArraySize)*minDynamicArraySize; + tmpData = (unsigned char *)NewP(newSize); + if (tmpData != NULL) { + memcpy(tmpData,dyn->data,dyn->used); + DisposeP((void **)&dyn->data); + dyn->size = newSize; + dyn->data = tmpData; + } + return tmpData != NULL; +} // AssertDynamicArraySize + +bool TermDynamicArray(GlyphTableArray *dyn) { + if (dyn->data != NULL) DisposeP((void **)&dyn->data); + dyn->size = 0; + return true; +} // TermDynamicArray + +typedef struct { + bool binaryOnly; + + int32_t numGlyphs,currGlyph; + + GlyphTableArray binary; + sfnt_HorizontalMetrics hmtx[maxNumGlyphs]; + + // for binaryOnly == false + GlyphTableArray asmSrc; + GlyphTableArray vttSrc; + + unsigned char glyfGroup[maxNumGlyphs]; + bool composite[maxNumGlyphs]; +} IncrBuildSfntData; + +bool TrueTypeFont::GetNumPointsAndContours(int32_t glyphIndex, int32_t *numKnots, int32_t *numContours, int32_t *componentDepth) { + unsigned char *data; + int16 contours,knots; + uint16 flags,glyph; + int32_t glyphSize; + + data = this->GetTablePointer(tag_GlyphData); + if (data == NULL) + return false; + + if (glyphIndex < 0 || this->numLocaEntries <= glyphIndex) + return false; + data += this->IndexToLoc[glyphIndex]; + + glyphSize = this->IndexToLoc[glyphIndex+1] - this->IndexToLoc[glyphIndex]; + if (glyphSize == 0) return true; // we've probably just added a range of empty glyphs in the process of importing glyphs + if (glyphSize < 0) + return false; // this would be bad, however.,. + + contours = READALIGNEDWORD(data); contours = SWAPW(contours); + data += 4*sizeof(uint16); // bounding box; + if (contours < 0) { // composite glyph + (*componentDepth)++; + if (*componentDepth >= MAXCOMPONENTSIZE) + return false; + do { + flags = READALIGNEDWORD(data); flags = SWAPW(flags); + glyph = READALIGNEDWORD(data); glyph = SWAPW(glyph); + if (!(GetNumPointsAndContours(glyph,numKnots,numContours,componentDepth))) + return false; + if (flags & ARG_1_AND_2_ARE_WORDS) + data += 2*sizeof(uint16); + else + data += sizeof(uint16); + if (flags & WE_HAVE_A_SCALE) + data += sizeof(uint16); + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + data += 2*sizeof(uint16); + else if (flags & WE_HAVE_A_TWO_BY_TWO) + data += 4*sizeof(uint16); + } while (flags & MORE_COMPONENTS); + } else { // (leaf) glyph + if (*numContours + (int32_t)contours >= MAXCONTOURS) + return false; + *numContours += (int32_t)contours; + if (contours > 0) { + data += (contours-1)*sizeof(uint16); + knots = READALIGNEDWORD(data); knots = SWAPW(knots) + 1; // look at end point array and add 1 + if (*numKnots + (int32_t)knots >= MAXPOINTS) + return false; + *numKnots += (int32_t)knots; + } + } + return true; // by now +} // TrueTypeFont::GetNumPointsAndContours + +bool TrueTypeFont::IncrBuildNewSfnt( wchar_t errMsg[], size_t errMsgLen) { +#define variableTablesForIncrBuild 10 + IncrBuildSfntData *iSfnt; + + errMsg[0] = L'\0'; + + if (this->incrBuildSfntData == NULL) + { + swprintf(errMsg, errMsgLen, L"this->incrBuildSfntData is NULL"); + return false; + } + iSfnt = (IncrBuildSfntData *)this->incrBuildSfntData; + + StripCommand strip = iSfnt->binaryOnly ? stripSource : stripNothing; + + unsigned char *sfnt, *tmpSfnt, *tmpSfntHandle; + + int32_t headerSize, i, j, j2, tag, numberOfGlyphs = 0, numberOfHMetrics = 0, zero = 0L, pad; + uint32_t sfntPos = 0, tmpSfntSize, sizeOfTable, newSfntSizeEstimate; + sfnt_FontHeader *head; + bool result = false; + + TableAvailability avail[variableTablesForIncrBuild]; + + avail[0] = TableAvailability(tag_ControlValue, true, stripHints); + avail[1] = TableAvailability(tag_FontProgram, true, stripHints); + avail[2] = TableAvailability(tag_PreProgram, true, stripHints); + avail[3] = TableAvailability(PRIVATE_GLIT1, true, stripSource); + avail[4] = TableAvailability(PRIVATE_PGM1, true, stripSource); + avail[5] = TableAvailability(PRIVATE_GLIT2, true, stripSource); + avail[6] = TableAvailability(PRIVATE_PGM2, true, stripSource); + avail[7] = TableAvailability(PRIVATE_GROUP, true, stripSource); + avail[8] = TableAvailability(tag_CVTVariations, this->IsCvarTupleData(), stripHints); + avail[9] = TableAvailability(PRIVATE_CVAR, this->IsCvarTupleData(), stripSource); + + head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); + if (SWAPL(head->magicNumber) != SFNT_MAGIC) { + swprintf(errMsg, errMsgLen, L"IncrBuildNewSfnt: Bad magic number in the head"); + goto term; + } + + numberOfGlyphs = this->NumberOfGlyphs(); + + this->PackMaxpHeadHhea(); + + sfnt = this->sfntHandle; // alias it + + if (!BuildOffsetTables(sfnt,this->sfntSize,this->offsetTable,this->tmpOffsetTable,variableTablesForIncrBuild,avail,strip,&headerSize)) { + swprintf(errMsg, errMsgLen, L"IncrBuildNewSfnt: offset is too large"); // claudebe, problem with a new font received from Sampo + goto term; + } + +#ifdef _DEBUG +// given MYOFFSETTABLESIZE for the size of the entire sfnt_OffsetTable, maxNumDirectoryEntries denotes +// the (truncated) number of sfnt_DirectoryEntry entries that this amount of memory is good for +// (see comments about inherited hack above) +#define maxNumDirectoryEntries ((MYOFFSETTABLESIZE - sizeof(sfnt_OffsetTable))/sizeof(sfnt_DirectoryEntry) + 1) + uint32_t sizeEstimate[maxNumDirectoryEntries]; + char tagCopy[maxNumDirectoryEntries][8]; + for (i = 0; i < maxNumDirectoryEntries; i++) { sizeEstimate[i] = 0; tagCopy[i][0] = 0; } +#endif + + // 1. pass: get a reasonnable upper limit for the expected file size + newSfntSizeEstimate = headerSize; + + for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { + + switch (tag = this->tmpOffsetTable->table[i].tag) { + + case tag_HorizontalMetrics: + // discovered in an old version of Courier New Italic, glyph "E" had a different AW than a composite using "E" (and E's metrics) + // which was correctly propagated to the hmtx upon PackGlyph, after which point the actual packing routine for the hmtx worked + // with a different numberOfHMetrics triggering an assert because a differing table size was detected + // Therefore, for size estimate always assume each glyph as a different AW + sizeOfTable = numberOfGlyphs*sizeof(sfnt_HorizontalMetrics); + break; + case tag_GlyphData: + sizeOfTable = iSfnt->binary.used; + break; + case tag_IndexToLoc: + // for size estimate, always assume long format 'loca' table; correct this->outShortIndexToLocTable determined in PackGlyph only + sizeOfTable = (numberOfGlyphs + 1)*sizeof(int32_t); + break; + case PRIVATE_GROUP: + sizeOfTable = numberOfGlyphs*sizeof(short); + break; + case PRIVATE_GLIT1: + case PRIVATE_GLIT2: + sizeOfTable = (numberOfGlyphs + GLIT_PAD)*sizeof(sfnt_FileDataEntry); + break; + case PRIVATE_CVAR: + sizeOfTable = this->tsicBinSize = this->EstimatePrivateCvar(); + break; + case PRIVATE_PGM1: + sizeOfTable = iSfnt->asmSrc.used; + break; + case PRIVATE_PGM2: + sizeOfTable = iSfnt->vttSrc.used; + break; + case tag_PreProgram: + sizeOfTable = this->binSize[asmPREP]; + break; + case tag_FontProgram: + sizeOfTable = this->binSize[asmFPGM]; + break; + case tag_ControlValue: + sizeOfTable = this->cvt->GetCvtBinarySize(); + break; + case tag_GridfitAndScanProc: + sizeOfTable = this->gaspBinSize ? this->gaspBinSize : this->tmpOffsetTable->table[i].length; + break; + case tag_CVTVariations: + this->cvarBinSize = this->EstimateCvar(); + sizeOfTable = this->cvarBinSize ? this->cvarBinSize : this->tmpOffsetTable->table[i].length; + break; + default: + sizeOfTable = this->tmpOffsetTable->table[i].length; + break; + } // switch (tag) + +#ifdef _DEBUG + tagCopy[i][0] = (char)(tag >> 24); + tagCopy[i][1] = (char)(tag >> 16); + tagCopy[i][2] = (char)(tag >> 8); + tagCopy[i][3] = (char)(tag); + tagCopy[i][4] = (char)0; +#endif + + sizeOfTable = DWordPad(sizeOfTable); + newSfntSizeEstimate += sizeOfTable; + } + + this->AssertMaxSfntSize(newSfntSizeEstimate,false,true); // make sure we have a large enough tmpSfnt + + if (newSfntSizeEstimate > this->maxTmpSfntSize) { + MaxSfntSizeError(L"IncrBuildNewSfnt: This font is getting too large",newSfntSizeEstimate,errMsg,errMsgLen); goto term; + } + + // 2. pass: build the new sfnt into the tmpSfntHandle + tmpSfnt = this->sfntTmpHandle; // alias this one, too + + sfntPos = headerSize; // skip header, need to fill directory first, then plug in header as final step + + for (i = 0; i < this->tmpOffsetTable->numOffsets; i++) { + + switch (tag = this->tmpOffsetTable->table[i].tag) { + + case tag_FontHeader: { + memcpy((char*) &tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); + head = (sfnt_FontHeader *)&tmpSfnt[sfntPos]; + long long temp_time = DateTime(); // ADD GREGH 20101209 + uint32_t temp_lower = (uint32_t)(temp_time & 0xFFFFFFFF); // ADD GREGH 20101209 + int32_t temp_upper = (int32_t)((temp_time >> 32) & 0xFFFFFFFF); // ADD GREGH 20101209 + head->modified.bc = SWAPL(temp_upper); // ADD GREGH 20101209 + head->modified.ad = SWAPL(temp_lower); // ADD GREGH 20101209 + + break; + } + case tag_HoriHeader: { // determine numberOfHMetrics, required in tag_HorizontalMetrics below!!! + int32_t aw; + sfnt_HorizontalHeader *hhea; + + memcpy((char *)&tmpSfnt[sfntPos],(char *)&sfnt[this->tmpOffsetTable->table[i].offset],this->tmpOffsetTable->table[i].length); + + aw = iSfnt->hmtx[numberOfGlyphs-1].advanceWidth; + for (numberOfHMetrics = numberOfGlyphs; numberOfHMetrics >= 2 && iSfnt->hmtx[numberOfHMetrics-2].advanceWidth == aw; numberOfHMetrics--); + + hhea = (sfnt_HorizontalHeader *)&tmpSfnt[sfntPos]; + hhea->numberOf_LongHorMetrics = CSWAPW((unsigned short)numberOfHMetrics); + break; + } + case tag_HorizontalMetrics: { + int32_t s,d; + unsigned short *hmtx; + + hmtx = (unsigned short *)&tmpSfnt[sfntPos]; + s = d = 0; + while (s < numberOfHMetrics) { + hmtx[d++] = SWAPW(iSfnt->hmtx[s].advanceWidth); + hmtx[d++] = SWAPW(iSfnt->hmtx[s].leftSideBearing); + s++; + } + while (s < numberOfGlyphs) { + // remainder of hmtx consists of lsb's only + hmtx[d++] = SWAPW(iSfnt->hmtx[s].leftSideBearing); + s++; + } + + this->tmpOffsetTable->table[i].length = d*sizeof(short); + break; + } + + case tag_GlyphData: { + this->tmpOffsetTable->table[i].length = iSfnt->binary.used; + memcpy(&tmpSfnt[sfntPos],iSfnt->binary.data,iSfnt->binary.used); + break; + } + case tag_IndexToLoc: + // here 'head' is (still) valid and pointing to the header of the sfnt being built + + this->outShortIndexToLocTable = iSfnt->binary.used < 65535*2; + if (this->outShortIndexToLocTable) { + unsigned short tempIdx; + unsigned short *shortIndexToLoc = (unsigned short *)&tmpSfnt[sfntPos]; + + this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(short); + head->indexToLocFormat = CSWAPW(SHORT_INDEX_TO_LOC_FORMAT); + for (j = 0; j < numberOfGlyphs; j++){ + tempIdx = (unsigned short)(iSfnt->binary.dataPos[j] >> 1); + shortIndexToLoc[j] = SWAPW(tempIdx); + } + tempIdx = (unsigned short)(iSfnt->binary.used >> 1); + shortIndexToLoc[numberOfGlyphs] = SWAPW(tempIdx); + } else { + int32_t *longIndexLoca = (int32_t *)&tmpSfnt[sfntPos]; + + this->tmpOffsetTable->table[i].length = (numberOfGlyphs + 1) * sizeof(int32_t); + head->indexToLocFormat = CSWAPW(LONG_INDEX_TO_LOC_FORMAT); + for (j = 0; j < numberOfGlyphs; j++) { + longIndexLoca[j] = SWAPL(iSfnt->binary.dataPos[j]); + } + longIndexLoca[numberOfGlyphs] = SWAPL(iSfnt->binary.used); + } + break; + + case PRIVATE_GROUP: { + int32_t j; + short *charGroup,tempGroup; + + this->tmpOffsetTable->table[i].length = numberOfGlyphs * sizeof(short); + charGroup = (short *)&tmpSfnt[sfntPos]; + for (j = 0; j < numberOfGlyphs; j++) { + tempGroup = iSfnt->glyfGroup[j]; + if (tempGroup < TOTALTYPE) tempGroup = charGroupToIntInFile[tempGroup]; // translate old char groups + charGroup[j] = SWAPW(tempGroup); + } + break; + } + + case PRIVATE_GLIT1: + case PRIVATE_GLIT2: { + int32_t glitEntries; + sfnt_MemDataEntry *memGlit; + GlyphTableArray *src; + + glitEntries = numberOfGlyphs + GLIT_PAD; + + if (tag == PRIVATE_GLIT1) { + memGlit = this->glit1; + src = &iSfnt->asmSrc; + } else { // tag == PRIVATE_GLIT2 + memGlit = this->glit2; + src = &iSfnt->vttSrc; + } + + for (j = 0; j < numberOfGlyphs; j++) { + memGlit[j].glyphCode = (unsigned short)j; + memGlit[j].offset = src->dataPos[j]; + memGlit[j].length = src->dataPos[j+1] - src->dataPos[j]; + } + for (j = numberOfGlyphs, j2 = 0; j2 < GLIT_PAD; j++, j2++) { + memGlit[j].glyphCode = glitPadIndex[j2]; + memGlit[j].offset = src->dataPos[j]; + memGlit[j].length = (j < glitEntries-1 ? src->dataPos[j+1] : src->used) - src->dataPos[j]; + } + memGlit[numberOfGlyphs].offset = PRIVATE_STAMP_1; // Stamp it; + + this->tmpOffsetTable->table[i].length = PackGlit((sfnt_FileDataEntry *)&tmpSfnt[sfntPos],glitEntries,memGlit); + break; + } + case PRIVATE_PGM1: { + this->tmpOffsetTable->table[i].length = iSfnt->asmSrc.used; + memcpy(&tmpSfnt[sfntPos],iSfnt->asmSrc.data,iSfnt->asmSrc.used); + break; + } + case PRIVATE_PGM2: { + this->tmpOffsetTable->table[i].length = iSfnt->vttSrc.used; + memcpy(&tmpSfnt[sfntPos],iSfnt->vttSrc.data,iSfnt->vttSrc.used); + break; + } + case tag_PreProgram: { + this->tmpOffsetTable->table[i].length = this->binSize[asmPREP]; + memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmPREP],this->binSize[asmPREP]); + break; + } + case tag_FontProgram: { + this->tmpOffsetTable->table[i].length = this->binSize[asmFPGM]; + memcpy((char*)&tmpSfnt[sfntPos],(char*)this->binData[asmFPGM],this->binSize[asmFPGM]); + break; + } + case tag_ControlValue: { + this->cvt->GetCvtBinary((int32_t*)&this->tmpOffsetTable->table[i].length,&tmpSfnt[sfntPos]); + break; + } + case PRIVATE_CVAR: + if (this->tsicBinSize) { + this->UpdatePrivateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); + } else { + memcpy((char*)&tmpSfnt[sfntPos], (char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); + } + break; + case tag_CVTVariations: + if (this->cvarBinSize) { + this->UpdateCvar((int32_t*)&this->tmpOffsetTable->table[i].length, &tmpSfnt[sfntPos]); + } else { + memcpy((char*)&tmpSfnt[sfntPos], (char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); + } + break; + case tag_GridfitAndScanProc: + if (this->gaspBinSize) { + unsigned short *packed; + int32_t s; + + this->tmpOffsetTable->table[i].length = this->gaspBinSize; + packed = (unsigned short *)&tmpSfnt[sfntPos]; + *packed++ = SWAPW(this->gaspTable.version); + *packed++ = SWAPW(this->gaspTable.numRanges); + for (s = 0; s < this->gaspTable.numRanges; s++) { + unsigned short rangeMaxPPEM = this->gaspTable.gaspRange.at(s).rangeMaxPPEM; + unsigned short rangeGaspBehavior = this->gaspTable.gaspRange.at(s).rangeGaspBehavior; + *packed++ = SWAPW(rangeMaxPPEM); + *packed++ = SWAPW(rangeGaspBehavior); + } + } else { + memcpy((char*)&tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); + } + break; + + default: { + memcpy((char*)&tmpSfnt[sfntPos],(char*)&sfnt[this->tmpOffsetTable->table[i].offset], this->tmpOffsetTable->table[i].length); + break; + } + } // switch (tag) + + this->tmpOffsetTable->table[i].offset = sfntPos; + sfntPos += this->tmpOffsetTable->table[i].length; + + pad = DWordPad(sfntPos) - sfntPos; + memcpy((char*)&tmpSfnt[sfntPos],(char*)&zero,pad); // zero pad for long word alignment + sfntPos += pad; + } +#ifdef _DEBUG + assert(newSfntSizeEstimate >= sfntPos); +#endif +// once we've finished plugging in all the tables, plug in the directory (temporary offset table) + + { sfnt_DirectoryEntry *entry; + + entry = &(this->tmpOffsetTable->table[0]); + for (i=0; itmpOffsetTable->numOffsets; i++, entry++) + { + entry->tag = SWAPL(entry->tag); + entry->checkSum = SWAPL(entry->checkSum); + entry->offset = SWAPL(entry->offset); + entry->length = SWAPL(entry->length); + } + this->tmpOffsetTable->version = SWAPL(this->tmpOffsetTable->version); + this->tmpOffsetTable->numOffsets = SWAPW(this->tmpOffsetTable->numOffsets); + this->tmpOffsetTable->searchRange = SWAPW(this->tmpOffsetTable->searchRange); + this->tmpOffsetTable->entrySelector = SWAPW(this->tmpOffsetTable->entrySelector); + this->tmpOffsetTable->rangeShift = SWAPW(this->tmpOffsetTable->rangeShift); + } + memcpy((char*)tmpSfnt,(char*)this->tmpOffsetTable, headerSize); + + result = true; // by now + +term: // function termination code + if (result) { // successful so far, now swap sfntTmpHandle <-> sfntHandle and install new sfnt; swap => no resize/handle deallocation/handle copy/memory fragmentation + tmpSfntHandle = this->sfntHandle; this->sfntHandle = this->sfntTmpHandle; this->sfntTmpHandle = tmpSfntHandle; + tmpSfntSize = this->maxSfntSize; this->maxSfntSize = this->maxTmpSfntSize; this->maxTmpSfntSize = tmpSfntSize; + this->sfntSize = sfntPos; + + this->CalculateNewCheckSums(); + this->CalculateCheckSumAdjustment(); + this->SortTableDirectory(); + + // claudebe the glit entries must be updated + this->glit1Entries = this->glit2Entries = 0; + this->shortIndexToLocTable = this->outShortIndexToLocTable; + + // adjust maxGlyphs to what we've stamped in PRIVATE_GLIT1 and PRIVATE_GLIT2 above + this->maxGlyphs = numberOfGlyphs; + + result = this->UnpackGlitsLoca(errMsg,errMsgLen) && this->UpdateMaxPointsAndContours(errMsg,errMsgLen) && this->UnpackCharGroup(errMsg,errMsgLen) && this->SetSfnt(-1,-1,errMsg,errMsgLen); + } + return result; + +} // TrueTypeFont::IncrBuildNewSfnt + +bool TrueTypeFont::InitIncrBuildSfnt(bool binaryOnly, wchar_t errMsg[], size_t errMsgLen) { + IncrBuildSfntData *iSfnt; + bool allocated; + + errMsg[0] = L'\0'; + + this->incrBuildSfntData = iSfnt = (IncrBuildSfntData *)NewP(sizeof(IncrBuildSfntData)); + if (iSfnt == NULL) + { + swprintf(errMsg, errMsgLen, L"Failed to allocate this->incrBuildSfntData"); + return false; + } + + iSfnt->binaryOnly = binaryOnly; + iSfnt->numGlyphs = this->NumberOfGlyphs(); + iSfnt->currGlyph = 0; + + allocated = InitDynamicArray(&iSfnt->binary); + if (allocated && !iSfnt->binaryOnly) allocated = InitDynamicArray(&iSfnt->asmSrc) && InitDynamicArray(&iSfnt->vttSrc); + + if (!allocated) + { + swprintf(errMsg, errMsgLen, L"Failed to allocate iSfnt dynamic arrays"); + return false; + } + + this->InitNewProfiles(); // TrueTypeFont keeps two sets of profiles, both are updated after any assembly, but only one is reset here + + return true; // by now +} // TrueTypeFont::InitIncrBuildSfnt + +void AddTextToDynArray(GlyphTableArray *dynArray, int32_t glyphIndex, TextBuffer *text) { + unsigned char *data; + size_t packedSize; + + data = &dynArray->data[dynArray->used]; + if (text != NULL) text->GetText(&packedSize,(char *)data); else packedSize = 0; + if (packedSize & 1) data[packedSize++] = '\x0d'; // CR-pad word align + dynArray->dataPos[glyphIndex] = dynArray->used; + dynArray->used += (int32_t)packedSize; +} // AddTextToDynArray + +bool TrueTypeFont::AddGlyphToNewSfnt(CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfBin, TextBuffer *glyfText, TextBuffer *talkText, wchar_t errMsg[], size_t errMsgLen) { + IncrBuildSfntData *iSfnt; + bool allocated; + unsigned char *data; + int32_t packedSize; + + errMsg[0] = L'\0'; + + if (this->incrBuildSfntData == NULL) + { + swprintf(errMsg, errMsgLen, L"this->incrBuildSfntData is NULL"); + return false; + } + iSfnt = (IncrBuildSfntData *)this->incrBuildSfntData; + + if (iSfnt->currGlyph != glyphIndex || glyphIndex < 0 || iSfnt->numGlyphs <= glyphIndex) { + swprintf(errMsg, errMsgLen, L"Calling TrueTypeFont::AddGlyphToNewSfnt out of sequence: expected glyph %li, received glyph %li", iSfnt->currGlyph, glyphIndex); + return false; + } + + iSfnt->glyfGroup[iSfnt->currGlyph] = (unsigned char)group; + + allocated = AssertDynamicArraySize(&iSfnt->binary,this->GetPackedGlyphSize(glyphIndex,glyph,glyfBinSize)); + if (allocated && !iSfnt->binaryOnly) + { + allocated = AssertDynamicArraySize(&iSfnt->asmSrc,glyfText->TheLengthInUTF8()) && AssertDynamicArraySize(&iSfnt->vttSrc,talkText->TheLengthInUTF8()); + } + + if (!allocated) + { + swprintf(errMsg, errMsgLen, L"Failed to assert iSfnt dynamic array sizes"); + return false; + } + + iSfnt->composite[iSfnt->currGlyph] = glyph->composite; + + data = &iSfnt->binary.data[iSfnt->binary.used]; + packedSize = this->PackGlyph(data,iSfnt->currGlyph,glyph,glyfBinSize,glyfBin,&iSfnt->hmtx[iSfnt->currGlyph]); + if (packedSize & 1) data[packedSize++] = '\x00'; // zero-pad word align + iSfnt->binary.dataPos[iSfnt->currGlyph] = iSfnt->binary.used; + iSfnt->binary.used += packedSize; + + if (!iSfnt->binaryOnly) { + AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph,glyfText); + AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph,talkText); + } + + iSfnt->currGlyph++; + + return true; // by now +} // TrueTypeFont::AddGlyphToNewSfnt + +bool TrueTypeFont::TermIncrBuildSfnt(bool disposeOnly, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *fpgmText, wchar_t errMsg[], size_t errMsgLen) { + // notice that it may seem inconsistent that in the above AddGlyphToNewSfnt we add both sources and binary data (of individual glyphs), + // while for TermIncrBuild we only add sources. This has to do with the weird architecture which, from the point of view of sources, + // understands prep, fpgm, and cvt as "special" glyphs at the end of the respective private table, while from the point of view of + // binary data, prep, fpgm, and cvt are individual TrueType tables. Not my first choice... + + IncrBuildSfntData *iSfnt; + bool done; + + if (this->incrBuildSfntData == NULL) { + // don't overwrite errMsg if we're merely called to "cleanup", see also calling pattern + if (!disposeOnly) swprintf(errMsg,errMsgLen,L"this->incrBuildSfntData is NULL"); + return false; + } + + iSfnt = (IncrBuildSfntData *)this->incrBuildSfntData; + + if (disposeOnly) { + // don't return true if we're merely called to "cleanup", see also calling pattern + done = false; + } else { + done = true; // assume + errMsg[0] = L'\0'; + + if (!iSfnt->binaryOnly) { + done = AssertDynamicArraySize(&iSfnt->asmSrc,prepText->TheLengthInUTF8() + cvtText->TheLengthInUTF8() + fpgmText->TheLengthInUTF8()); + if (done) { + AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph, NULL); // dummy for future stamp + AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+1,prepText); // the sources for the prep, fpgm, cvt, and parm + AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+2,fpgmText); // must follow the same sequence as glitPadIndex!!! + AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+3,cvtText); + AddTextToDynArray(&iSfnt->asmSrc,iSfnt->currGlyph+4,NULL); + + AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph, NULL); // dummy for future stamp + AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+1,NULL); // more dummies + AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+2,NULL); + AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+3,NULL); + AddTextToDynArray(&iSfnt->vttSrc,iSfnt->currGlyph+4,NULL); + } else { + swprintf(errMsg,errMsgLen,L"Failed to assert iSfnt dynamic array sizes"); + } + } + + + if (done) { + this->UseNewProfiles(); // copy new profiles into current ones and pack into current sfnt + done = this->IncrBuildNewSfnt(errMsg, errMsgLen); + } + + } + + if (!iSfnt->binaryOnly) { + TermDynamicArray(&iSfnt->vttSrc); + TermDynamicArray(&iSfnt->asmSrc); + } + TermDynamicArray(&iSfnt->binary); + + DisposeP((void **)&iSfnt); + this->incrBuildSfntData = NULL; + + return done; +} // TrueTypeFont::TermIncrBuildSfnt + +sfnt_maxProfileTable TrueTypeFont::GetProfile(void) { + return this->profile; +} // TrueTypeFont::GetProfile + +int32_t TrueTypeFont::GetUnitsPerEm(void) { // FUnits Per EM (2048 is typical) + return this->unitsPerEm; +} // TrueTypeFont::GetUnitsPerEm + +void TrueTypeFont::InitNewProfiles(void) { + this->newProfile.version = 0x00010000; + this->newProfile.numGlyphs = this->profile.numGlyphs; + this->newProfile.maxPoints = 0; + this->newProfile.maxContours = 0; + this->newProfile.maxCompositePoints = 0; + this->newProfile.maxCompositeContours = 0; + + // those values shouldn't be updated in recalc 'maxp', only reset during autohinting GregH 8/23/95 + // this->newProfile.maxElements = this->profile.maxElements; + // this->newProfile.maxTwilightPoints = this->profile.maxTwilightPoints; + // this->newProfile.maxStorage = this->profile.maxStorage; + + // as per ClaudeBe 6/30/97 we can safely assume these minima + this->newProfile.maxElements = Max(MIN_ELEMENTS,this->profile.maxElements); + this->newProfile.maxTwilightPoints = Max(MIN_TWILIGHTPOINTS,this->profile.maxTwilightPoints); + this->newProfile.maxStorage = Max(MIN_STORAGE,this->profile.maxStorage); + + this->newProfile.maxFunctionDefs = this->profile.maxFunctionDefs; + this->newProfile.maxInstructionDefs = this->profile.maxInstructionDefs; + this->newProfile.maxStackElements = 0; + this->newProfile.maxSizeOfInstructions = 0; + this->newProfile.maxComponentElements = 0; + this->newProfile.maxComponentDepth = 0; + // see comments in TrueTypeFont::UpdateAssemblerProfile + for (int32_t i = 0; i < Len(this->maxStackElements); i++) this->maxStackElements[i] = 0; + + this->newMetricProfile.xMin = 0x7fff; + this->newMetricProfile.yMin = 0x7fff; + this->newMetricProfile.xMax = (short)0x8000; + this->newMetricProfile.yMax = (short)0x8000; + + this->newMetricProfile.advanceWidthMax = (short)0x8000; + this->newMetricProfile.minLeftSideBearing = (short)0x7fff; + this->newMetricProfile.minRightSideBearing = 0x7fff; + this->newMetricProfile.xMaxExtent = (short)0x8000; +} // TrueTypeFont::InitNewProfiles + +void TrueTypeFont::InheritProfiles(void) +{ + this->newProfile = this->profile; +} + +void TrueTypeFont::UseNewProfiles(void) { + + this->profile = this->newProfile; + this->metricProfile = this->newMetricProfile; + this->PackMaxpHeadHhea(); +} // TrueTypeFont::UseNewProfiles + +void TrueTypeFont::UpdateGlyphProfile(TrueTypeGlyph *glyph) { + short numberOfPoints = glyph->numContoursInGlyph ? glyph->endPoint[glyph->numContoursInGlyph-1] + 1 : 0; + + if (!glyph->composite) { // cf. TT specs, "maxp": Max points/contours in a non-composite glyph + this->profile.maxPoints = Max(this->profile.maxPoints,numberOfPoints); + this->newProfile.maxPoints = Max(this->newProfile.maxPoints,numberOfPoints); + this->profile.maxContours = Max(this->profile.maxContours,(short)glyph->numContoursInGlyph); + this->newProfile.maxContours = Max(this->newProfile.maxContours,(short)glyph->numContoursInGlyph); + } +} // TrueTypeFont::UpdateGlyphProfile + +void TrueTypeFont::UpdateMetricProfile(TrueTypeGlyph *glyph) { + short end = glyph->numContoursInGlyph > 0 ? glyph->endPoint[glyph->numContoursInGlyph-1] : -1, + advanceWidth = glyph->x[end + PHANTOMPOINTS] - glyph->x[end + PHANTOMPOINTS - 1]; // get whatever the rasterizer has deemed correct in terms of composite and useMyMetrics + + this->metricProfile.xMin = Min(this->metricProfile.xMin, glyph->xmin); + this->newMetricProfile.xMin = Min(this->newMetricProfile.xMin,glyph->xmin); + this->metricProfile.xMax = Max(this->metricProfile.xMax, glyph->xmax); + this->newMetricProfile.xMax = Max(this->newMetricProfile.xMax,glyph->xmax); + this->metricProfile.yMin = Min(this->metricProfile.yMin, glyph->ymin); + this->newMetricProfile.yMin = Min(this->newMetricProfile.yMin,glyph->ymin); + this->metricProfile.yMax = Max(this->metricProfile.yMax, glyph->ymax); + this->newMetricProfile.yMax = Max(this->newMetricProfile.yMax,glyph->ymax); + + this->metricProfile.minLeftSideBearing = Min(this->metricProfile.minLeftSideBearing, glyph->xmin); + this->newMetricProfile.minLeftSideBearing = Min(this->newMetricProfile.minLeftSideBearing, glyph->xmin); + this->metricProfile.minRightSideBearing = Min(this->metricProfile.minRightSideBearing, advanceWidth - glyph->xmax); + this->newMetricProfile.minRightSideBearing = Min(this->newMetricProfile.minRightSideBearing,advanceWidth - glyph->xmax); + this->metricProfile.advanceWidthMax = Max(this->metricProfile.advanceWidthMax, advanceWidth); + this->newMetricProfile.advanceWidthMax = Max(this->newMetricProfile.advanceWidthMax, advanceWidth); + this->metricProfile.xMaxExtent = Max(this->metricProfile.xMaxExtent, glyph->xmax); + this->newMetricProfile.xMaxExtent = Max(this->newMetricProfile.xMaxExtent, glyph->xmax); +} // TrueTypeFont::UpdateMetricProfile + +void TrueTypeFont::UpdateAssemblerProfile(ASMType asmType, short maxFunctionDefs, short maxStackElements, short maxSizeOfInstructions) { + uint16 maxStackElementsPrepFpgm,maxStackElementsGlyfFpgm; + + this->profile.maxFunctionDefs = Max(this->profile.maxFunctionDefs,maxFunctionDefs); + this->newProfile.maxFunctionDefs = Max(this->newProfile.maxFunctionDefs,maxFunctionDefs); + + this->profile.maxStackElements = Max(this->profile.maxStackElements,maxStackElements); // <--- relevant during individual assembly + + // Coming up with a good max value for the number of stack elements used at any one time + // is a bit of a heuristic, because we cannot possibly test all glyphs against all sizes + // and all combinations of rendering modes (black-and-white, grey-scaled, many versions + // of cleartype) and all variants of the rasterizer. If there were no branches, loops, or + // calls, then it would be straightforward to determine an upper limit for the fpgm (or + // any function therein), for the prep, and for the maximum of all upper limits for any + // of the glyfs. For branches, the argument could be made that each branch will have to + // affect the stack depth in the same way, else the respective code will terminate with + // an imbalance (stack underflow, or extra stack elements left over). To account for that + // we can safely take the maximum of all branches (and recursively for branches within + // branches). For loops theoretically each iteration could grow the stack, and the number + // of iterations need not be known by the assembler, so this could theoretically lead to + // unbound growth. This is a much less likely scenario though because I have not seen a + // great many fonts with iterations, and the described scenario would again be an erro- + // neous piece of code. Function calls, in simple cases, typically do not call other + // functions, so the safe upper limit would be to take the deepest stack of any glyf and + // add to that the deepest stack resulting from any function call, or the maximum depth + // resulting from executing the prep and add to that the deepest stack resulting from any + // function call. Calls within calls, and much less likely, recursive calls, would again + // falsify this assumption, but bar testing the whole matrix mentioned above, we cannot + // think of a better solution at this time. Notice, finally, that we assume that the + // TTAssembler, which produces values for maxStackElements upon assembling the fpgm, + // prep, or any of the glyfs, chooses the smartest strategy it can, but we didn't verify + // this assumption because the code is a little bit too chaotic to understand what it + // really does... + + // new heuristic for re-calc maxp or compiling/assembling everything for all glyphs only!!! + + // notice that in order to handle this->profile.maxStackElements in much the same way as + // this->newProfile.maxStackElements, we'd have to store maxp values for maxStackElements + // separately, for each of the fpgm, prep, and glyf. Since historically we don't, what + // comes in when opening a font is a this->profile.maxStackElements that is either updated + // according to the old method, or it is a max of a sum of parts we don't know anymore. + // Therefore this is the best we can do. Any problems produced by the incremental method + // (such as after a newly assembled glyf) can always be corrected by re-calculating the + // maxp from scratch. + + this->maxStackElements[asmType] = Max(this->maxStackElements[asmType],maxStackElements); + // from both the deepest level in the prep or any glyf we could call a function, hence add the respective maxima + maxStackElementsPrepFpgm = this->maxStackElements[asmPREP] + this->maxStackElements[asmFPGM]; + maxStackElementsGlyfFpgm = this->maxStackElements[asmGLYF] + this->maxStackElements[asmFPGM]; + // the max stack depth then becomes the maximum of the two intermediate maxima above + this->newProfile.maxStackElements = Max(maxStackElementsPrepFpgm,maxStackElementsGlyfFpgm); // <--- relevant during complete assembly/re-calc maxp + + this->profile.maxSizeOfInstructions = Max(this->profile.maxSizeOfInstructions,maxSizeOfInstructions); + this->newProfile.maxSizeOfInstructions = Max(this->newProfile.maxSizeOfInstructions,maxSizeOfInstructions); +} // TrueTypeFont::UpdateAssemblerProfile + +void TrueTypeFont::UpdateAutohinterProfile(short maxElements, short maxTwilightPoints, short maxStorage) { + // for autohinter pre-program + this->profile.maxFunctionDefs = 0; + this->newProfile.maxFunctionDefs = 0; + + this->profile.maxInstructionDefs = 0; + this->newProfile.maxInstructionDefs = 0; + + this->profile.maxElements = maxElements; + this->newProfile.maxElements = maxElements; + + this->profile.maxTwilightPoints = maxTwilightPoints; + this->newProfile.maxTwilightPoints = maxTwilightPoints; + + this->profile.maxStorage = maxStorage; + this->newProfile.maxStorage = maxStorage; +} // TrueTypeFont::UpdateAutohinterProfile + +bool TrueTypeFont::HasSource() +{ + return (this->glit1Entries != 0) && (this->glit2Entries != 0); +} + +bool TrueTypeFont::ReverseInterpolateCvarTuples() +{ + bool result = false; + + if (!this->IsVariationFont()) + return true; // all done + + + auto instanceManager = this->GetInstanceManager(); + auto cvarTuples = instanceManager->GetCvarTuples(); + + std::vector defaultCvts; + this->GetDefaultCvts(defaultCvts); + + // Iterate through tuples + for (uint16_t index = 0; index < cvarTuples->size(); ++index) + { + auto& editedCvtsFromTuple = cvarTuples->at(index)->editedCvtValues; + auto& interpolatedCvtsFromTuple = cvarTuples->at(index)->interpolatedCvtValues; + + // Assert size of editedCvtValue with defaultCvt size + editedCvtsFromTuple.resize(defaultCvts.size(), Variation::EditedCvtValue()); + // Initialize interpolatedCvtValues with defaultCvt values + interpolatedCvtsFromTuple.resize(defaultCvts.size(), Variation::InterpolatedCvtValue()); + for (size_t cvt = 0; cvt < defaultCvts.size(); ++cvt) + { + interpolatedCvtsFromTuple[cvt] = Variation::InterpolatedCvtValue(defaultCvts[cvt]); + } + + for (size_t cvt = 0; cvt < defaultCvts.size(); cvt++) + { + if (editedCvtsFromTuple.at(cvt).Edited()) + { + interpolatedCvtsFromTuple[cvt].Update(editedCvtsFromTuple.at(cvt).Value()); + } + } + + } + + result = cvtVariationInterpolator_->ReverseInterpolate(defaultCvts, axisCount_, *cvarTuples); + + return result; +} + + +template +bool almost_equal(T x, T y, int ulp) +{ + return std::abs(x - y) <= ulp || std::abs(x + y) <= ulp; +} + +#define ULP 2 +#define FLAGS_CANT_CHANGE_VAR (ARG_1_AND_2_ARE_WORDS | ARGS_ARE_XY_VALUES | ROUND_XY_TO_GRID | WE_HAVE_A_SCALE | MORE_COMPONENTS | WE_HAVE_AN_X_AND_Y_SCALE | WE_HAVE_A_TWO_BY_TWO | WE_HAVE_A_TWO_BY_TWO) + +CheckCompositeResult CheckCompositeVariationCompatible(const short* pFirst, short firstSize, const short* pSecond, short secondSize) +{ + TrueTypeCompositeComponent first, second; + int i1 = 0, i2 = 0; + + if (pFirst == nullptr || pSecond == nullptr || firstSize != secondSize) + return CheckCompositeResult::Fail; + + do { + const unsigned short noChangeFlags = FLAGS_CANT_CHANGE_VAR; + first.flags = pFirst[i1++]; first.flags = SWAPW(first.flags); + second.flags = pSecond[i2++]; second.flags = SWAPW(second.flags); + + if ((first.flags & noChangeFlags) != (second.flags & noChangeFlags)) + return CheckCompositeResult::Fail; + + first.glyphIndex = pFirst[i1++]; first.glyphIndex = SWAPW(first.glyphIndex); + second.glyphIndex = pSecond[i2++]; second.glyphIndex = SWAPW(second.glyphIndex); + + if (first.glyphIndex != second.glyphIndex) + return CheckCompositeResult::Fail; + + if (first.flags & ARG_1_AND_2_ARE_WORDS) + { + first.arg1 = pFirst[i1++]; first.arg1 = SWAPW(first.arg1); + second.arg1 = pSecond[i2++]; second.arg1 = SWAPW(second.arg1); + + first.arg2 = pFirst[i1++]; first.arg2 = SWAPW(first.arg2); + second.arg2 = pSecond[i2++]; second.arg2 = SWAPW(second.arg2); + + if (first.arg1 != second.arg1 || first.arg2 != second.arg2) + return CheckCompositeResult::Fail; + } + else + { + // both args are packed in a uint16 so treat the same since we only compare about comparison + first.arg1 = pFirst[i1++]; + second.arg1 = pSecond[i2++]; + + if (first.arg1 != second.arg1) + return CheckCompositeResult::Fail; + } + + if (first.flags & WE_HAVE_A_SCALE) + { + first.xscale = pFirst[i1++]; first.xscale = SWAPW(first.xscale); + second.xscale = pSecond[i2++]; second.xscale = SWAPW(second.xscale); + + if (first.xscale == second.xscale) + return CheckCompositeResult::Success; + else if (almost_equal(first.xscale, second.xscale, ULP)) + return CheckCompositeResult::Tolerance; + else return CheckCompositeResult::Fail; + } + else if (first.flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + first.xscale = pFirst[i1++]; first.xscale = SWAPW(first.xscale); + second.xscale = pSecond[i2++]; second.xscale = SWAPW(second.xscale); + + first.yscale = pFirst[i1++]; first.yscale = SWAPW(first.yscale); + second.yscale = pSecond[i2++]; second.yscale = SWAPW(second.yscale); + + if (first.xscale == second.xscale && first.yscale == second.yscale) + return CheckCompositeResult::Success; + else if(almost_equal(first.xscale, second.xscale, ULP) && almost_equal(first.yscale, second.yscale, ULP)) + return CheckCompositeResult::Tolerance; + else return CheckCompositeResult::Fail; + } + else if (first.flags & WE_HAVE_A_TWO_BY_TWO) + { + first.xscale = pFirst[i1++]; first.xscale = SWAPW(first.xscale); + second.xscale = pSecond[i2++]; second.xscale = SWAPW(second.xscale); + + first.scale01 = pFirst[i1++]; first.scale01 = SWAPW(first.scale01); + second.scale01 = pSecond[i2++]; second.scale01 = SWAPW(second.scale01); + + first.scale10 = pFirst[i1++]; first.scale10 = SWAPW(first.scale10); + second.scale10 = pSecond[i2++]; second.scale10 = SWAPW(second.scale10); + + first.yscale = pFirst[i1++]; first.yscale = SWAPW(first.yscale); + second.yscale = pSecond[i2++]; second.yscale = SWAPW(second.yscale); + + if (first.xscale == second.xscale && first.yscale == second.yscale && first.scale01 == second.scale01 && first.scale10 == second.scale10) + return CheckCompositeResult::Success; + else if (almost_equal(first.xscale, second.xscale, ULP) && almost_equal(first.yscale, second.yscale, ULP) && almost_equal(first.scale01, second.scale01, ULP) && almost_equal(first.scale10, second.scale10, ULP)) + return CheckCompositeResult::Tolerance; + else return CheckCompositeResult::Fail; + } + + } while (first.flags & MORE_COMPONENTS); + + assert(i1 == i2); + assert(i1 <= firstSize); + + return CheckCompositeResult::Success; +} + +/* + * update the contenents in the glyph structure: glyph->componentData + * startOfLinePtr : for error spot indication + * context = CO_CompInstrFollow if more Composite line followed; + * context = CO_StdInstrFollow if more instructions followed; + * context = CO_NothingFollows otherwise; + * round : boolean variable [r] or [R]; + * name : instruction name. + * args : arguments storage + * argc : number of arguments. + * return "true" if sucessful, otherwise return "false" + */ + +void TrueTypeFont::UpdateCompositeProfile(TrueTypeGlyph *glyph, TTCompositeProfile *compositeProfile, short context, short RoundingCode, short InstructionIndex, short *args, short argc, sfnt_glyphbbox *Newbbox, short *error) { + short i, tmpShort,flags = 0, contours, points; + unsigned short glyphIndex; + + short ComponentDepth; + sfnt_glyphbbox sub_bbox, tmpbbox; + + + glyph->ComponentVersionNumber = -1; /* Magic number */ + + if ( compositeProfile->nextExitOffset ) { + *error = co_AnchorNothingAbove; + return; + } + + + switch (InstructionIndex ) { + case OVERLAPIndex: + compositeProfile->GlobalNON_OVERLAPPING = 0; + if ( context != CO_CompInstrFollow ) *error = co_OverlapLastInstruction; + return; + break; + case NONOVERLAPIndex: + compositeProfile->GlobalNON_OVERLAPPING = 1; + if ( context != CO_CompInstrFollow ) *error = co_NonOverlapLastInstruction; + return; + break; + case USEMYMETRICSIndex: + compositeProfile->GlobalUSEMYMETRICS = 1; + if ( context != CO_CompInstrFollow ) *error = co_UseMymetricsLastInstruction; + return; + break; + case SCALEDCOMPONENTOFFSETIndex: + compositeProfile->GlobalSCALEDCOMPONENTOFFSET = 1; + if ( context != CO_CompInstrFollow ) *error = co_ScaledComponentOffsetLastInstruction; + if ( compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET ) *error = co_UnscaledComponentOffsetAlreadySet; + return; + break; + case UNSCALEDCOMPONENTOFFSETIndex: + compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET = 1; + if ( context != CO_CompInstrFollow ) *error = co_UnscaledComponentOffsetLastInstruction; + if ( compositeProfile->GlobalSCALEDCOMPONENTOFFSET ) *error = co_ScaledComponentOffsetAlreadySet; + return; + break; + + case ANCHORIndex: + case SANCHORIndex: + if (!this->GetNumberOfPointsAndContours(args[0], &contours, &points, &ComponentDepth,&sub_bbox)) { + *error = co_ComponentSizeOverflow; + return; + } + if ( args[1] > compositeProfile->anchorPrevPoints || args[2] > points ) { + *error = co_AnchorArgExceedMax; + return; + } + break; + } + + + // if ( compositeProfile->GlobalNON_OVERLAPPING ) flags |= NON_OVERLAPPING; else flags &= (~NON_OVERLAPPING); + // per Apple's and Microsoft's specs, and per GregH (27 Jan 2003): this flag should always be set to 0 (zero) + flags &= (~NON_OVERLAPPING); + if ( compositeProfile->GlobalUSEMYMETRICS ) flags |= USE_MY_METRICS; else flags &= (~USE_MY_METRICS); + if ( compositeProfile->GlobalUSEMYMETRICS ) compositeProfile->GlobalUSEMYMETRICS = 0; + if ( compositeProfile->GlobalSCALEDCOMPONENTOFFSET ) flags |= SCALED_COMPONENT_OFFSET; + if ( compositeProfile->GlobalSCALEDCOMPONENTOFFSET ) compositeProfile->GlobalSCALEDCOMPONENTOFFSET = 0; + if ( compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET ) flags |= UNSCALED_COMPONENT_OFFSET; + if ( compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET ) compositeProfile->GlobalUNSCALEDCOMPONENTOFFSET = 0; + + if (InstructionIndex == OFFSETIndex || InstructionIndex == SOFFSETIndex) + { + flags |= ARGS_ARE_XY_VALUES; + for ( i = 1; i <= 2; i++ ) { + if ( args[i] < -128 || args[i] > 127 ) { + flags |= ARG_1_AND_2_ARE_WORDS; + break; + } + } + } else { + for ( i = 1; i <= 2; i++ ) { + if ( args[i] < 0 || args[i] > 255 ) { + flags |= ARG_1_AND_2_ARE_WORDS; + break; + } + } + } + + if ( RoundingCode ) flags |= ROUND_XY_TO_GRID; + + + + if ( argc == 7 ) { + /* the ConvertDoubleToShort conversion is done now during parsing */ + if ( args[4] == 0 && args[5] == 0) { + if ( args[3] == args[6] ) { + flags |= WE_HAVE_A_SCALE; + } else { + flags |= WE_HAVE_AN_X_AND_Y_SCALE; + } + } else { + flags |= WE_HAVE_A_TWO_BY_TWO; + } + } + + if ( context == CO_CompInstrFollow ) flags |= MORE_COMPONENTS; else flags &= (~MORE_COMPONENTS); + if ( context == CO_StdInstrFollow ) { + flags |= WE_HAVE_INSTRUCTIONS; + compositeProfile->nextExitOffset = true; + } + + if (compositeProfile->GlobalMORE_COMPONENTS) { + compositeProfile->numberOfCompositeElements++; + glyph->componentData[ glyph->componentSize++] = SWAPW(flags); + + glyphIndex = (unsigned short)args[0]; + glyph->componentData[ glyph->componentSize++] = SWAPW(glyphIndex); + if ( flags & ARG_1_AND_2_ARE_WORDS ) { + /* arg 1 and 2 */ + glyph->componentData[ glyph->componentSize++] = SWAPW(args[1]); + glyph->componentData[ glyph->componentSize++] = SWAPW(args[2]); + } else { + /* arg 1 and 2 as bytes */ + unsigned char *uByte = (unsigned char *)&glyph->componentData[glyph->componentSize]; + + uByte[0] = (unsigned char)args[1]; + uByte[1] = (unsigned char)args[2]; + glyph->componentSize++; + } + /* scale */ + if ( flags & WE_HAVE_A_SCALE ) { + glyph->componentData[ glyph->componentSize++] = SWAPW(args[3]); + } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { + glyph->componentData[ glyph->componentSize++] = SWAPW(args[3]); + glyph->componentData[ glyph->componentSize++] = SWAPW(args[6]); + } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) { + /* xscale, scale01, scale10, yscale */ + glyph->componentData[ glyph->componentSize++] = SWAPW(args[3]); + glyph->componentData[ glyph->componentSize++] = SWAPW(args[4]); + glyph->componentData[ glyph->componentSize++] = SWAPW(args[5]); + glyph->componentData[ glyph->componentSize++] = SWAPW(args[6]); + } + if ( glyph->componentSize >= MAXCOMPONENTSIZE ) { + *error = co_ComponentSizeOverflow; + return; + } + + this->GetNumberOfPointsAndContours(glyphIndex, &contours, &points, &ComponentDepth,&sub_bbox ); + /* claudebe, update the bounding box, inspired by sfnaccs.c, ReadComponentData */ + + if (flags & (WE_HAVE_A_SCALE | WE_HAVE_AN_X_AND_Y_SCALE | WE_HAVE_A_TWO_BY_TWO)) + { + if (flags & WE_HAVE_A_TWO_BY_TWO) + { + + if ( args[3] == 0 && args[6] == 0) { /* we have a 90degree rotation, we can compute the new bbox just using the old one */ + + tmpbbox = sub_bbox; + sub_bbox.xmin = (short) ( ((int32_t)tmpbbox.ymin * (int32_t)args[5]) >> 14); + sub_bbox.xmax = (short) ( ((int32_t)tmpbbox.ymax * (int32_t)args[5]) >> 14); + sub_bbox.ymin = (short) ( ((int32_t)tmpbbox.xmin * (int32_t)args[4]) >> 14); + sub_bbox.ymax = (short) ( ((int32_t)tmpbbox.xmax * (int32_t)args[4]) >> 14); + + if (sub_bbox.xmin > sub_bbox.xmax) { /* there was a negative scaling */ + tmpShort = sub_bbox.xmin; + sub_bbox.xmin = sub_bbox.xmax; + sub_bbox.xmax = tmpShort; + } + if (sub_bbox.ymin > sub_bbox.ymax) { /* there was a negative scaling */ + tmpShort = sub_bbox.ymin; + sub_bbox.ymin = sub_bbox.ymax; + sub_bbox.ymax = tmpShort; + } + + /* first scale, then apply the offset */ + sub_bbox.xmin += args[1]; + sub_bbox.xmax += args[1]; + sub_bbox.ymin += args[2]; + sub_bbox.ymax += args[2]; + + Newbbox->xmin = Min(Newbbox->xmin,sub_bbox.xmin); + Newbbox->ymin = Min(Newbbox->ymin,sub_bbox.ymin); + Newbbox->xmax = Max(Newbbox->xmax,sub_bbox.xmax); + Newbbox->ymax = Max(Newbbox->ymax,sub_bbox.ymax); + + } else { +// edit_ErrorMessage( L"Warning, DovMan doesn't know how to compute the glyph bounding box when rotating a glyph", glyph->componentSize ); + } + + } + else + { + /* If we have a scale factor */ + + + if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + sub_bbox.xmin = (short) ( ((int32_t)sub_bbox.xmin * (int32_t)args[3]) >> 14); + sub_bbox.xmax = (short) ( ((int32_t)sub_bbox.xmax * (int32_t)args[3]) >> 14); + sub_bbox.ymin = (short) ( ((int32_t)sub_bbox.ymin * (int32_t)args[6]) >> 14); + sub_bbox.ymax = (short) ( ((int32_t)sub_bbox.ymax * (int32_t)args[6]) >> 14); + } + else + { + sub_bbox.xmin = (short) ( ((int32_t)sub_bbox.xmin * (int32_t)args[3]) >> 14); + sub_bbox.xmax = (short) ( ((int32_t)sub_bbox.xmax * (int32_t)args[3]) >> 14); + sub_bbox.ymin = (short) ( ((int32_t)sub_bbox.ymin * (int32_t)args[3]) >> 14); + sub_bbox.ymax = (short) ( ((int32_t)sub_bbox.ymax * (int32_t)args[3]) >> 14); + } + + if (sub_bbox.xmin > sub_bbox.xmax) { /* there was a negative scaling */ + tmpShort = sub_bbox.xmin; + sub_bbox.xmin = sub_bbox.xmax; + sub_bbox.xmax = tmpShort; + } + if (sub_bbox.ymin > sub_bbox.ymax) { /* there was a negative scaling */ + tmpShort = sub_bbox.ymin; + sub_bbox.ymin = sub_bbox.ymax; + sub_bbox.ymax = tmpShort; + } + + /* first scale, then apply the offset */ + sub_bbox.xmin += args[1]; + sub_bbox.xmax += args[1]; + sub_bbox.ymin += args[2]; + sub_bbox.ymax += args[2]; + + Newbbox->xmin = Min(Newbbox->xmin,sub_bbox.xmin); + Newbbox->ymin = Min(Newbbox->ymin,sub_bbox.ymin); + Newbbox->xmax = Max(Newbbox->xmax,sub_bbox.xmax); + Newbbox->ymax = Max(Newbbox->ymax,sub_bbox.ymax); + } + } else { + + if (flags & ARGS_ARE_XY_VALUES) + { + /* I don't neet to test ARG_1_AND_2_ARE_WORDS as I can get the arguments directely from args[] */ + sub_bbox.xmin += args[1]; + sub_bbox.xmax += args[1]; + sub_bbox.ymin += args[2]; + sub_bbox.ymax += args[2]; + + Newbbox->xmin = Min(Newbbox->xmin,sub_bbox.xmin); + Newbbox->ymin = Min(Newbbox->ymin,sub_bbox.ymin); + Newbbox->xmax = Max(Newbbox->xmax,sub_bbox.xmax); + Newbbox->ymax = Max(Newbbox->ymax,sub_bbox.ymax); + } + else + { + /* ClaudeBe, I'm not handling the anchor point case now */ + sub_bbox.xmin = sub_bbox.xmin; /* to set a break point */ + } + } + + compositeProfile->anchorPrevPoints += points; + compositeProfile->numberOfCompositeContours += contours; + compositeProfile->numberOfCompositePoints += points; + if ( compositeProfile->numberOfCompositeContours > this->profile.maxCompositeContours ) { + this->profile.maxCompositeContours = compositeProfile->numberOfCompositeContours; + } + if ( compositeProfile->numberOfCompositeContours > this->newProfile.maxCompositeContours ) { + this->newProfile.maxCompositeContours = compositeProfile->numberOfCompositeContours; + } + if ( compositeProfile->numberOfCompositePoints > this->profile.maxCompositePoints ) { + this->profile.maxCompositePoints = compositeProfile->numberOfCompositePoints; + } + if ( compositeProfile->numberOfCompositePoints > this->newProfile.maxCompositePoints ) { + this->newProfile.maxCompositePoints = compositeProfile->numberOfCompositePoints; + } + if ( compositeProfile->numberOfCompositeElements > this->profile.maxComponentElements ) { + this->profile.maxComponentElements = compositeProfile->numberOfCompositeElements; + } + if ( compositeProfile->numberOfCompositeElements > this->newProfile.maxComponentElements ) { + this->newProfile.maxComponentElements = compositeProfile->numberOfCompositeElements; + } + if ( ComponentDepth+1 > this->profile.maxComponentDepth ) { + this->profile.maxComponentDepth = ComponentDepth+1; + } + if ( ComponentDepth+1 > this->newProfile.maxComponentDepth ) { + this->newProfile.maxComponentDepth = ComponentDepth+1; + } + + } + compositeProfile->GlobalMORE_COMPONENTS = (flags & MORE_COMPONENTS ); + + return; +} // TrueTypeFont::UpdateCompositeProfile + +void TrueTypeFont::CalculateNewCheckSums(void) { +// for each table in font, calculate table's checksum + unsigned char *sfnt = this->sfntHandle; + sfnt_OffsetTable *directory; + sfnt_DirectoryEntry *entry; + unsigned short numTables; + uint32_t *loopStart,checkSum,*loopEnd,*loop,i, length; + sfnt_FontHeader *head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); + + head->checkSumAdjustment = 0; // checksum of head table to be computed with this set to 0 + directory = (sfnt_OffsetTable*)sfnt; + numTables = SWAPW(directory->numOffsets); + for (i = 0; i < numTables; i++) { + entry = &directory->table[i]; + loopStart = (uint32_t *)(sfnt + SWAPL(entry->offset)); + length = SWAPL(entry->length); + loopEnd = loopStart + DWordPad(length)/sizeof(uint32_t); + checkSum = 0; + for (loop = loopStart; loop < loopEnd; loop++) + { + checkSum += SWAPL(*loop); + }; + entry->checkSum = SWAPL(checkSum); + } +} // TrueTypeFont::CalculateNewCheckSums + +void TrueTypeFont::CalculateCheckSumAdjustment(void) { + + unsigned char *sfnt = this->sfntHandle; + sfnt_FontHeader *head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); + uint32_t *loopStart,checkSum,*loopEnd,*loop; + + loopStart = (uint32_t *)sfnt; + loopEnd = loopStart + DWordPad(this->sfntSize)/sizeof(uint32_t); + for (loop = loopStart, checkSum = 0; loop < loopEnd; loop++) + { + checkSum += SWAPL(*loop); + } + head->checkSumAdjustment = CSWAPL(0xb1b0afba - checkSum); +} // TrueTypeFont::CalculateCheckSumAdjustment + +void TrueTypeFont::SortTableDirectory(void) { + + sfnt_OffsetTable *directory = (sfnt_OffsetTable *)this->sfntHandle; + sfnt_DirectoryEntry *entry = directory->table,temp; + unsigned short numTables = SWAPW(directory->numOffsets); + int32_t i,j; + bool swap; + + swap = true; + for (i = numTables - 1; i > 0 && swap; i--) { // Bubble-Sort + swap = false; + for (j = 0; j < i; j++) { + if (SWAPL(entry[j].tag) > SWAPL(entry[j+1].tag)) { + swap = true; + temp = entry[j]; + entry[j] = entry[j+1]; + entry[j+1] = temp; + } + } + } +} // TrueTypeFont::SortTableDirectory + +void TrueTypeFont::PackMaxpHeadHhea(void) { + sfnt_FontHeader *head; + sfnt_HorizontalHeader *hhea; + + PackMaxp(this->GetTablePointer(tag_MaxProfile),&this->profile); + + head = (sfnt_FontHeader *)this->GetTablePointer(tag_FontHeader); + head->macStyle = SWAPW(this->macStyle); + + head->xMin = SWAPW(this->metricProfile.xMin); + head->yMin = SWAPW(this->metricProfile.yMin); + head->xMax = SWAPW(this->metricProfile.xMax); + head->yMax = SWAPW(this->metricProfile.yMax); + + hhea = (sfnt_HorizontalHeader *)this->GetTablePointer(tag_HoriHeader); + + hhea->advanceWidthMax = SWAPW(this->metricProfile.advanceWidthMax); + hhea->minLeftSideBearing = SWAPW(this->metricProfile.minLeftSideBearing); + hhea->minRightSideBearing = SWAPW(this->metricProfile.minRightSideBearing); + hhea->xMaxExtent = SWAPW(this->metricProfile.xMaxExtent); +} // TrueTypeFont::PackMaxpHeadHhea + +uint32_t TrueTypeFont::GetPackedGlyphsSizeEstimate(TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc) { +// for the purpose of asserting a big enough buffer (this->sfntHandle) it should be enough to determine a +// reasonnable upper limit. Between two invocations of BuildNewSfnt, at most one glyph is changed. The +// size of this change is calculated exactly. For all other glyphs we use the current size (difference +// of position in oldIndexToLoca), regardless of the fact that the font may actually be stripped of its +// code in the process of building the new sfnt + + uint32_t length,size; + int32_t i,numEntries; //numContours; + + numEntries = this->NumberOfGlyphs(); + + size = 0; + for (i = 0; i < numEntries; i++) { + if (i == glyphIndex) { + length = this->GetPackedGlyphSize(glyphIndex,glyph,this->binSize[asmGLYF]); + } else { + length = oldIndexToLoc[i+1] - oldIndexToLoc[i]; + } + + size += length; + if (size & 1) size++; // zero-pad word align + } + return size; +} // TrueTypeFont::GetPackedGlyphsSizeEstimate + +uint32_t TrueTypeFont::GetPackedGlyphSize(int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize) { + uint32_t size; + short i, numberOfPoints, x, y, delta, j; + unsigned char bitFlags; + + size = 0; + + if (glyph->numContoursInGlyph == 0 || (glyph->numContoursInGlyph == 1 && glyph->startPoint[0] == 0 && glyph->endPoint[0] == 0 && glyph->x[0] == 0 && glyph->y[0] == 0 && !glyph->onCurve[0] && glyfBinSize == 0)) { + return size; // assume this is a dummy point that is neither instructed nor serves as an anchor, hence turn into an empty glyph by leaving with size == 0... + } + + // by now, we have at least one control point + numberOfPoints = glyph->endPoint[glyph->numContoursInGlyph - 1] + 1; // excluding the side bearing points + + if ( glyph->componentSize > 0 ) { + + size += 5*sizeof(short) + sizeof(short)*glyph->componentSize; // component version number, bbox + if (glyfBinSize > 0) size += sizeof(short) + glyfBinSize; // glyfBinSize + + } else { + + size += 5*sizeof(short) + glyph->numContoursInGlyph*sizeof(short) + sizeof(short) + glyfBinSize; // number of contours, bbox, glyfBinSize + + // Calculate flags + for ( x = y = i = 0; i < numberOfPoints; i++ ) { + bitFlags = glyph->onCurve[i] & ONCURVE; + delta = glyph->x[i] - x; x = glyph->x[i]; + if ( delta == 0 ) { + bitFlags |= NEXT_X_IS_ZERO; + } else if ( delta >= -255 && delta <= 255 ) { + bitFlags |= XSHORT; + if ( delta > 0 ) + bitFlags |= SHORT_X_IS_POS; + } + + delta = glyph->y[i] - y; y = glyph->y[i]; + if ( delta == 0 ) { + bitFlags |= NEXT_Y_IS_ZERO; + } else if ( delta >= -255 && delta <= 255 ) { + bitFlags |= YSHORT; + if ( delta > 0 ) + bitFlags |= SHORT_Y_IS_POS; + } + this->tmpFlags[i] = bitFlags; + } + + // Write out bitFlags + for ( i = 0; i < numberOfPoints;) { + unsigned char repeat = 0; + for (j = i+1; j < numberOfPoints && this->tmpFlags[i] == this->tmpFlags[j] && repeat < 255; j++) + repeat++; + if (repeat > 1) { + size += 2; + i = j; + } else { + size++; + i++; + } + } + + // Write out X + for (x = i = 0; i < numberOfPoints; i++) { + delta = glyph->x[i] - x; x = glyph->x[i]; + if (this->tmpFlags[i] & XSHORT) { + if (!(this->tmpFlags[i] & SHORT_X_IS_POS)) delta = -delta; + size++; + } else if (!(this->tmpFlags[i] & NEXT_X_IS_ZERO)) + size += 2; + } + + // Write out Y + for (y = i = 0; i < numberOfPoints; i++) { + delta = glyph->y[i] - y; y = glyph->y[i]; + if (this->tmpFlags[i] & YSHORT) { + if (!(this->tmpFlags[i] & SHORT_Y_IS_POS)) delta = -delta; + size++; + } else if (!(this->tmpFlags[i] & NEXT_Y_IS_ZERO)) + size += 2; + } + } + + return size; // by now +} // TrueTypeFont::GetPackedGlyphSize + +uint32_t TrueTypeFont::PackGlyphs(StripCommand strip, TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc, uint32_t *newIndexToLoc, unsigned char *dst) { + uint32_t length,size; + int32_t i,numEntries; //numContours; + unsigned char *src; + + numEntries = this->NumberOfGlyphs(); + + src = this->GetTablePointer(tag_GlyphData); + + size = 0; + for (i = 0; i < numEntries; i++) { + newIndexToLoc[i] = size; + + if (i == glyphIndex) { + length = this->PackGlyph(&dst[size],glyphIndex,glyph,this->binSize[asmGLYF],this->binData[asmGLYF],&this->horMetric[glyphIndex]); + } else { + length = oldIndexToLoc[i+1] - oldIndexToLoc[i]; + if (length) { + if (strip < stripHints) + memcpy(&dst[size],&src[oldIndexToLoc[i]],length); + else + length = this->StripGlyphBinary(&dst[size],&src[oldIndexToLoc[i]],length); + } + } + + size += length; + if (size & 1) dst[(size)++] = 0; // zero-pad word align + } + newIndexToLoc[i] = size; + + this->outShortIndexToLocTable = size < 65535*2; + + return size; +} // TrueTypeFont::PackGlyphs + +uint32_t TrueTypeFont::PackGlyph(unsigned char *dst, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfInstruction, sfnt_HorizontalMetrics *hmtx) { + unsigned char *pStart; + uint32_t size; + short i, numberOfPoints, x, y, delta, j, whoseMetrics = 0; + unsigned char bitFlags; + bool composite,useMyMetrics; + + size = 0; + +// if (glyph->numContoursInGlyph == 0) { // fake empty glyph with 1 knot; must be off-curve or else we don't get back the side-bearing points from the apple rasterizer... +// for (i = PHANTOMPOINTS; i > 0; i--) { +// glyph->x[i] = glyph->x[i-1]; +// glyph->y[i] = glyph->y[i-1]; +// glyph->onCurve[i] = glyph->onCurve[i-1]; +// } +// glyph->startPoint[0] = glyph->endPoint[0] = glyph->x[0] = glyph->y[0] = glyph->onCurve[0] = false; +// glyph->numContoursInGlyph++; +// } + + if (glyph->numContoursInGlyph == 0 || (glyph->numContoursInGlyph == 1 && glyph->startPoint[0] == 0 && glyph->endPoint[0] == 0 && glyph->x[0] == 0 && glyph->y[0] == 0 && !glyph->onCurve[0] && glyfBinSize == 0)) { + hmtx->leftSideBearing = 0; + hmtx->advanceWidth = glyph->x[glyph->numContoursInGlyph + 1] - glyph->x[glyph->numContoursInGlyph]; + return 0; // assume this is a dummy point that is neither instructed nor serves as an anchor, hence turn into an empty glyph by leaving with size == 0... + } + + // by now, we have at least one control point + numberOfPoints = glyph->endPoint[glyph->numContoursInGlyph - 1] + 1; // excluding the side bearing points + + pStart = dst; + + composite = useMyMetrics = false; + if ( glyph->componentSize > 0 ) { + int32_t len = 0; + short * flags = nullptr; + + composite = true; + + WRITEALIGNEDWORD( dst, SWAPW(glyph->ComponentVersionNumber) ); + + /* bbox */ + WRITEALIGNEDWORD( dst, SWAPW(glyph->xmin) ); + WRITEALIGNEDWORD( dst, SWAPW(glyph->ymin) ); + WRITEALIGNEDWORD( dst, SWAPW(glyph->xmax) ); + WRITEALIGNEDWORD( dst, SWAPW(glyph->ymax) ); + + i = 0; + do { + flags = &glyph->componentData[i++]; + if (*flags & CSWAPW(USE_MY_METRICS)) { + useMyMetrics = true; + whoseMetrics = glyph->componentData[i++]; + + whoseMetrics = SWAPW(whoseMetrics); + } else + i++; // skip glyphIndex + if (*flags & CSWAPW(ARG_1_AND_2_ARE_WORDS)) i+=2; else i++; + + if (*flags & CSWAPW(WE_HAVE_A_TWO_BY_TWO)) i+=4; + else if (*flags & CSWAPW(WE_HAVE_A_SCALE)) i++; + else if (*flags & CSWAPW(WE_HAVE_AN_X_AND_Y_SCALE)) i+=2; + } while (*flags & CSWAPW(MORE_COMPONENTS)); + + // glyfBinSize may be binary of plain TrueType code or binary of instructions added to composites + // for the purpose of stripping instructions, glyfBinSize is set to 0, after which the flag in the flags + // byte (that is interspersed in the composite code) is inconsistent, hence the above "parsing" and the check below... + if (*flags & CSWAPW(WE_HAVE_INSTRUCTIONS) && glyfBinSize == 0) *flags &= ~CSWAPW(WE_HAVE_INSTRUCTIONS); + + len = glyph->componentSize + glyph->componentSize; + memcpy( (char *) dst,(char *)glyph->componentData, len ); + + dst += len; + if (glyfBinSize > 0) { + + WRITEALIGNEDWORD( dst, (short)SWAPW(glyfBinSize) ); + memcpy((char*)dst,(char*)glyfInstruction,glyfBinSize); // this is already big-endian + dst += glyfBinSize; + } + } else { + short xmin,ymin,xmax,ymax; + + /* bbox and sidebearings */ + xmin = 0x7fff; + ymin = 0x7fff; + xmax = (short)0x8000; + ymax = (short)0x8000; + for ( i = 0; i < numberOfPoints; i++ ) { + xmin = Min(xmin,glyph->x[i]); + xmax = Max(xmax,glyph->x[i]); + ymin = Min(ymin,glyph->y[i]); + ymax = Max(ymax,glyph->y[i]); + } + glyph->xmin = xmin; + glyph->ymin = ymin; + glyph->xmax = xmax; + glyph->ymax = ymax; + + WRITEALIGNEDWORD( dst, SWAPW(glyph->numContoursInGlyph) ); + + WRITEALIGNEDWORD( dst, SWAPW(glyph->xmin) ); + WRITEALIGNEDWORD( dst, SWAPW(glyph->ymin) ); + WRITEALIGNEDWORD( dst, SWAPW(glyph->xmax) ); + WRITEALIGNEDWORD( dst, SWAPW(glyph->ymax) ); + + for ( i = 0; i < glyph->numContoursInGlyph; i++ ) { + WRITEALIGNEDWORD( dst, SWAPW(glyph->endPoint[i]) ); + } + + WRITEALIGNEDWORD( dst, (short)SWAPW(glyfBinSize) ); + memcpy((char*)dst,(char*)glyfInstruction,glyfBinSize); + dst += glyfBinSize; + + /* Calculate flags */ + for ( x = y = i = 0; i < numberOfPoints; i++ ) { + bitFlags = glyph->onCurve[i] & ONCURVE; + delta = glyph->x[i] - x; x = glyph->x[i]; + if ( delta == 0 ) { + bitFlags |= NEXT_X_IS_ZERO; + } else if ( delta >= -255 && delta <= 255 ) { + bitFlags |= XSHORT; + if ( delta > 0 ) + bitFlags |= SHORT_X_IS_POS; + } + + delta = glyph->y[i] - y; y = glyph->y[i]; + if ( delta == 0 ) { + bitFlags |= NEXT_Y_IS_ZERO; + } else if ( delta >= -255 && delta <= 255 ) { + bitFlags |= YSHORT; + if ( delta > 0 ) + bitFlags |= SHORT_Y_IS_POS; + } + this->tmpFlags[i] = bitFlags; + } + + /* Write out bitFlags */ + for ( i = 0; i < numberOfPoints;) { + unsigned char repeat = 0; + for ( j = i+1; j < numberOfPoints && this->tmpFlags[i] == this->tmpFlags[j] && repeat < 255; j++ ) + repeat++; + if ( repeat > 1 ) { + *dst++ = this->tmpFlags[i] | REPEAT_FLAGS; + *dst++ = repeat; + i = j; + } else { + *dst++ = this->tmpFlags[i++]; + } + } + + /* Write out X */ + for ( x = i = 0; i < numberOfPoints; i++ ) { + delta = glyph->x[i] - x; x = glyph->x[i]; + if ( this->tmpFlags[i] & XSHORT ) { + if ( !(this->tmpFlags[i] & SHORT_X_IS_POS) ) delta = -delta; + *dst++ = (unsigned char) delta; + } else if ( !(this->tmpFlags[i] & NEXT_X_IS_ZERO ) ) + WRITENONALIGNEDWORD( dst, delta ); + } + + /* Write out Y */ + for ( y = i = 0; i < numberOfPoints; i++ ) { + delta = glyph->y[i] - y; y = glyph->y[i]; + if ( this->tmpFlags[i] & YSHORT ) { + if ( !(this->tmpFlags[i] & SHORT_Y_IS_POS) ) delta = -delta; + *dst++ = (unsigned char) delta; + } else if ( !(this->tmpFlags[i] & NEXT_Y_IS_ZERO ) ) + WRITENONALIGNEDWORD( dst, delta ); + } + } + + hmtx->leftSideBearing = glyph->xmin - glyph->x[numberOfPoints]; // some rather oldish fonts have x[LSB] != 0 + hmtx->advanceWidth = useMyMetrics ? this->horMetric[whoseMetrics].advanceWidth : glyph->x[numberOfPoints + 1] - glyph->x[numberOfPoints]; + + size = (short)(ptrdiff_t)(dst - pStart); + return size; +} // TrueTypeFont::PackGlyph + +uint32_t TrueTypeFont::StripGlyphBinary(unsigned char *dst, unsigned char *src, uint32_t srcLen) { + unsigned char *glyfPos, *flagPos; + short numContours,i; + unsigned short flags, binLen; + + if (!srcLen) return 0; // nothing else we could squeeze out of it... + + glyfPos = src; + + numContours = COPYALIGNEDWORD(dst,src); + + numContours = SWAPW(numContours); + COPYALIGNEDWORD(dst,src); + COPYALIGNEDWORD(dst,src); + COPYALIGNEDWORD(dst,src); + COPYALIGNEDWORD(dst,src); // skip bounding box + + if (numContours < 0) { + binLen = 0; // assume so far + do { + flagPos = dst; + flags = COPYALIGNEDWORD(dst,src); + + flags = SWAPW(flags); + i = 1; // skip glyphIndex + if (flags & ARG_1_AND_2_ARE_WORDS) i += 2; else i++; + if (flags & WE_HAVE_A_SCALE) i++; + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) i += 2; + else if (flags & WE_HAVE_A_TWO_BY_TWO) i += 4; + while (i > 0) { + COPYALIGNEDWORD(dst,src); i--; + } + } while (flags & MORE_COMPONENTS); + + if (flags & WE_HAVE_INSTRUCTIONS) { + binLen = READALIGNEDWORD(src); + + binLen = SWAPW(binLen); + flags &= ~WE_HAVE_INSTRUCTIONS; + WRITEALIGNEDWORD(flagPos,SWAPW(flags)); // reset flag, cf. PackGlyph + } + } else { + while (numContours > 0) { COPYALIGNEDWORD(dst,src); numContours--; } // skip end points array + binLen = READALIGNEDWORD(src); + + binLen = SWAPW(binLen); + + WRITEALIGNEDWORD(dst,0); + memcpy((char*)dst,(char*)(src + binLen),glyfPos + srcLen - src - binLen); + } + return srcLen - binLen; +} // TrueTypeFont::StripGlyphBinary + +uint32_t TrueTypeFont::GetPackedGlyphSourceSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, int32_t glitIndex, sfnt_MemDataEntry *memGlit) { + uint32_t size; + + size = 0; + + if (type == 1) { + if (memGlit[glitIndex].glyphCode == PRE_PGM_GLYPH_INDEX) size = prepText->TheLengthInUTF8(); + else if (memGlit[glitIndex].glyphCode == CVT_GLYPH_INDEX) size = cvtText ->TheLengthInUTF8(); + else if (memGlit[glitIndex].glyphCode == HL_GLYPH_INDEX) size = 0; + else if (memGlit[glitIndex].glyphCode == FONT_PGM_GLYPH_INDEX) size = fpgmText->TheLengthInUTF8(); + else if (memGlit[glitIndex].glyphCode == glyphIndex) size = glyfText->TheLengthInUTF8(); + else if (glitIndex < this->maxGlyphs) size = memGlit[glitIndex].length; + } else { // type == 2 + if (memGlit[glitIndex].glyphCode == glyphIndex) size = talkText->TheLengthInUTF8(); + else if (glitIndex < this->maxGlyphs) size = memGlit[glitIndex].length; + } + + if (size & 1) size++; // word align + + return size; +} // TrueTypeFont::GetPackedGlyphSourceSize + +uint32_t TrueTypeFont::GetPackedGlyphSourcesSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, sfnt_MemDataEntry *memGlit) { + uint32_t size; + int32_t glitIndex; + + size = 0; + + for (glitIndex = 0; glitIndex < this->maxGlyphs; glitIndex++) + size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,glyphIndex,glitIndex,memGlit); + + size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,PRE_PGM_GLYPH_INDEX, this->maxGlyphs+1,memGlit); + size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,CVT_GLYPH_INDEX, this->maxGlyphs+2,memGlit); + size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,HL_GLYPH_INDEX, this->maxGlyphs+3,memGlit); + size += this->GetPackedGlyphSourceSize(glyfText,prepText,cvtText,talkText,fpgmText,type,FONT_PGM_GLYPH_INDEX,this->maxGlyphs+4,memGlit); + + return size; +} // TrueTypeFont::GetPackedGlyphSourcesSize + +void TrueTypeFont::PackGlyphSource(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, int32_t glitIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, + uint32_t *dstPos, unsigned char *dst) { + +// B.St.: Notice that PackGlyph, PackGlyphs and probably various such methods do not check against overflowing the sfnt being built in a timely manner, +// i.e. not until it is too late in BuildNewSfnt. This needs some improvements I didn't get around to doing when re-engineering TypeMan/DovMan/TTED. + + if (type == 1) { + if (memGlit[glitIndex].glyphCode == PRE_PGM_GLYPH_INDEX) prepText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); + else if (memGlit[glitIndex].glyphCode == CVT_GLYPH_INDEX) cvtText ->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); + else if (memGlit[glitIndex].glyphCode == HL_GLYPH_INDEX) memGlit[glitIndex].length = 0; + else if (memGlit[glitIndex].glyphCode == FONT_PGM_GLYPH_INDEX) fpgmText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); + else if (memGlit[glitIndex].glyphCode == glyphIndex) glyfText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); + else if (glitIndex < this->maxGlyphs) memcpy((char *)&dst[*dstPos], (char *)(this->GetTablePointer(PRIVATE_PGM1) + memGlit[glitIndex].offset), memGlit[glitIndex].length); // fetch binary + } else { // type == 2 + if (memGlit[glitIndex].glyphCode == glyphIndex) talkText->GetText((size_t*)&memGlit[glitIndex].length,(char *)&dst[*dstPos]); + else if (glitIndex < this->maxGlyphs) memcpy((char *)&dst[*dstPos], (char *)(this->GetTablePointer(PRIVATE_PGM2) + memGlit[glitIndex].offset), memGlit[glitIndex].length); + } + fileGlit[glitIndex].offset = SWAPL(*dstPos); + + { + int32_t length = memGlit[glitIndex].length > 0x8000 ? 0x8000 : (unsigned short)memGlit[glitIndex].length; + fileGlit[glitIndex].length = SWAPW(length); + } + *dstPos += memGlit[glitIndex].length; + if (*dstPos & 1) dst[(*dstPos)++] = '\x0D'; // CR-pad (in case length is derived from two consecutive (even!) offsets) word align +} // TrueTypeFont::PackGlyphSource + +void TrueTypeFont::PackGlyphSources(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, + uint32_t *dstLen, unsigned char *dst) { + int32_t glitIndex; + + *dstLen = 0; + for (glitIndex = 0; glitIndex < this->maxGlyphs; glitIndex++) + { + this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,glyphIndex,glitIndex,fileGlit,memGlit,dstLen,dst); + } + this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,PRE_PGM_GLYPH_INDEX, this->maxGlyphs+1,fileGlit,memGlit,dstLen,dst); + this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,CVT_GLYPH_INDEX, this->maxGlyphs+2,fileGlit,memGlit,dstLen,dst); + this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,HL_GLYPH_INDEX, this->maxGlyphs+3,fileGlit,memGlit,dstLen,dst); + this->PackGlyphSource(glyfText,prepText,cvtText,talkText,fpgmText,type,FONT_PGM_GLYPH_INDEX,this->maxGlyphs+4,fileGlit,memGlit,dstLen,dst); +} // TrueTypeFont::PackGlyphSources + +/* + * retuns contours and points + */ +bool TrueTypeFont::SubGetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox) { + short i, cont; + unsigned char *sp = 0 <= glyphIndex && glyphIndex < this->numLocaEntries ? (unsigned char *)this->GetTablePointer(tag_GlyphData) + this->IndexToLoc[glyphIndex] : NULL; + short tmp; + unsigned short flags; + sfnt_glyphbbox Sub_bbox; + short NbOfPoints = 0; + + if (sp) { + cont = READALIGNEDWORD( sp ); + + cont = SWAPW(cont); + + bbox->xmin = READALIGNEDWORD( sp ); + + bbox->xmin = SWAPW(bbox->xmin); + bbox->ymin = READALIGNEDWORD( sp ); + + bbox->ymin = SWAPW(bbox->ymin); + bbox->xmax = READALIGNEDWORD( sp ); + + bbox->xmax = SWAPW(bbox->xmax); + bbox->ymax = READALIGNEDWORD( sp ); + + bbox->ymax = SWAPW(bbox->ymax); + + if ( cont < 0 ) { + * ComponentDepth += 1; + if ( * ComponentDepth >= MAXCOMPONENTSIZE ) return false; + do { + + flags = READALIGNEDWORD( sp ); + + flags = SWAPW(flags); + + glyphIndex = READALIGNEDWORD( sp ); + + glyphIndex = SWAPW(glyphIndex); + if (!this->SubGetNumberOfPointsAndContours(glyphIndex,contours, points, ComponentDepth, &Sub_bbox)) return false; + if ( flags & ARG_1_AND_2_ARE_WORDS ) { + /* arg 1 and 2 */ + tmp = READALIGNEDWORD( sp ); + tmp = READALIGNEDWORD( sp ); + } else { + /* arg 1 and 2 as bytes */ + tmp = READALIGNEDWORD( sp ); + } + + if ( flags & WE_HAVE_A_SCALE ) { + /* scale */ + tmp = READALIGNEDWORD( sp ); + } else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) { + /* xscale, yscale */ + tmp = READALIGNEDWORD( sp ); + tmp = READALIGNEDWORD( sp ); + } else if ( flags & WE_HAVE_A_TWO_BY_TWO ) { + /* xscale, scale01, scale10, yscale */ + tmp = READALIGNEDWORD( sp ); + tmp = READALIGNEDWORD( sp ); + tmp = READALIGNEDWORD( sp ); + tmp = READALIGNEDWORD( sp ); + } + } while ( flags & MORE_COMPONENTS ); + + } else { + *contours += cont; + for ( i = 0; i < cont; i++ ) { +// claudebe the info is an array of last point number + + tmp = READALIGNEDWORD( sp ); + NbOfPoints = SWAPW(tmp) + 1; /* read endpoints */ + } + if ((int32_t)(*points) + (int32_t)NbOfPoints >= MAXPOINTS) return false; + *points += NbOfPoints; + } + } + return true; +} // TrueTypeFont::SubGetNumberOfPointsAndContours + +bool TrueTypeFont::GetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox) { + *contours = 0; + *points = 0; + *ComponentDepth = 0; + /* claudebe call recursively this->SubGetNumberOfPointsAndContours and add the number of contours and points + of all the components and the subcomponents of composite ghyphs */ + return this->SubGetNumberOfPointsAndContours(glyphIndex,contours, points, ComponentDepth, bbox); +} // TrueTypeFont::GetNumberOfPointsAndContours + + + diff --git a/src/TTFont.h b/src/TTFont.h index 5427784..c82bd5e 100644 --- a/src/TTFont.h +++ b/src/TTFont.h @@ -1,565 +1,565 @@ -/// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef TTFont_dot_h -#define TTFont_dot_h - -#define trialNumGlyphs 0x0400 -#define trialSfntSize 0x200000 // bytes - -#include -#include -#include -#include -#include -#include - -#include "opentypedefs.h" -#include "CvtManager.h" - -#define MAXCONTOURS 0x2000 -#define MAXPOINTS 0x4000 // rasterizer limit, needs extra two bits for line transition information in drop-out control algorithm - - //#define PHANTOMPOINTS 4 -#define PHANTOMPOINTS 2 - -#define LEFTSIDEBEARING 0 -#define RIGHTSIDEBEARING 1 - -//#define TOPSIDEBEARING 2 -//#define BOTTOMSIDEBEARING 3 - -#define INVALID_GLYPH_INDEX 0xffff - -#define maxNumCharCodes 0x10000 - -#define GASP_VERSION_DEFAULT 0x0000 -#define GASP_VERSION_CT_YAA 0x0001 -#define MAX_GASP_ENTRIES 6 -#define GASP_GRIDFIT 0x0001 -#define GASP_DOGRAY 0x0002 -#define GASP_CLEARTYPEGRIDFIT 0x0004 -#define GASP_DONONCTDIRAA 0x0008 - -namespace TtFont -{ - typedef struct { - unsigned short rangeMaxPPEM; - unsigned short rangeGaspBehavior; - } GaspRange; - - typedef struct { - unsigned short version; - unsigned short numRanges; - std::vector gaspRange; - } GaspTable; - - typedef struct { - unsigned short version; - unsigned short numRanges; - GaspRange gaspRange[1]; - } GaspTableRaw; -} - -typedef struct { - short xmin, ymin, xmax, ymax; -} sfnt_glyphbbox; - -typedef struct { - unsigned short CharCode; - CharGroup type; -} GlyphGroupType; - -// 'glit' (glyph index) table -typedef struct { - unsigned short glyphCode; - unsigned short length; - int32_t offset; -} sfnt_FileDataEntry; - -typedef struct { - unsigned short glyphCode; - uint32_t length; - int32_t offset; -} sfnt_MemDataEntry; -/****** what we have here are two pairs of private tables TS00 through TS03, the first pair for TT sources, -the second pair for TMT sources. Within each pair, the first is merely an index into the second, i.e. TT of -glyph n starts at TS01[TS00[n].offset] and TMT of glyph n starts at TS03[TS02[n].offset], assuming that we -are talking byte offsets here, and TS01 and TS03 are just arrays of bytes. The fact that we have two dif- -ferent structs here could be hidden in the sfnt module: in the font files we may have a short form of the -'glit' table, wherein all the offsets are short (sfnt_FileDataEntry), restricting the size, but in the rest -of the application we want to work only with the long offsets (sfnt_MemDataEntry) *****/ - -typedef struct { - short xMin, yMin, xMax, yMax; // rectilinear convex hull of all glyphs (union of all glyphs' bounding boxes) - short advanceWidthMax; - short minLeftSideBearing; - short minRightSideBearing; - short xMaxExtent; -} FontMetricProfile; - -struct VariationAxisRecord -{ - uint32_t axisTag; - Fixed16_16 minValue; - Fixed16_16 defaultValue; - Fixed16_16 maxValue; - uint16_t flags; - uint16_t nameID; -}; - -struct VariationInstanceRecord -{ - uint16_t nameID; - uint16_t flags; - std::vector< Fixed16_16> coordinates; - //uint16_t postScriptNameID; -}; - -struct FVarTableHeader -{ - uint16_t majorVersion; - uint16_t minorVersion; - uint16_t offsetToAxesArray; - uint16_t countSizePairs; - uint16_t axisCount; - uint16_t axisSize; - uint16_t instanceCount; - uint16_t instanceSize; - std::vector axisRecords; - std::vector< VariationInstanceRecord> instanceRecords; -}; - -struct ShortFracCorrespondence -{ - Fixed2_14 fromCoord; //A normalized coordinate value obtained using default normalization. - Fixed2_14 toCoord; //The modified, normalized coordinate value. -}; - -struct ShortFracSegment -{ - uint16_t pairCount; // The number of correspondence pairs for this axis. - std::vector axisValueMaps; // The array of axis value map records for this axis. -}; - -struct AxisVariationHeader -{ - uint16_t majorVersion; - uint16_t minorVersion; - uint16_t reserved; - uint16_t axisCount; - std::vector axisSegmentMaps; // The segment maps array - one segment map for each axis -}; - -typedef struct { - uint16_t majorVersion; - uint16_t minorVersion; - uint16_t tupleVariationCount; - uint16_t offsetToData; -} CvarHeader; - -struct TSICRecord { - uint16_t flags = 0; - uint16_t numCVTEntries = 0; - uint16_t nameLength = 0; - std::wstring name; // uint16_t NameArray[nameLength]; - std::vector cvts; // uint16_t CVTArray[numCVTEntries]; - std::vector cvtValues; // FWORD CVTValueArray[numCVTEntries]; -}; - -struct TSICHeader { - uint16_t majorVersion = 0; - uint16_t minorVersion = 0; - uint16_t flags = 0; - uint16_t axisCount = 0; - uint16_t recordCount = 0; - uint16_t reserved = 0; - std::vector axes; //uint32_t AxisArray[axisCount]; - std::vector> locations; //uint16_t RecordLocations[recordCount][axisCount]; - std::vector tsicRecords; //TSICRecord TSICRecord[recordCount]; -}; - -typedef struct { - uint16_t cvt; - int16_t delta; -} CvtElement; - -#define MAXCVT maxCvtNum - -#define MAXBINSIZE (0x10000L - 4) // don't push the limit - -#define MAXCOMPONENTSIZE 256 -#define MAXNUMCOMPONENTS (MAXCOMPONENTSIZE/3) // flags, glyphIndex, pair of bytes for offset = minimal size used by each component - -// these values to assert 'maxp' ok with templates (possibly generated by auto-hinter) -#define MIN_STORAGE 47 -#define MIN_TWILIGHTPOINTS 16 -#define MIN_ELEMENTS 2 - -// This is the magic cookie for the preferences. -#define MAGIC 0xA2340987 // note that this is a different magic number than SFNT_MAGIC... - -typedef struct { - uint32_t unicode; - unsigned short glyphIndex; -} UniGlyphMap; - -typedef struct { - uint32_t unicode; - unsigned short glyphIndex; - unsigned short numContours; - bool rounded; - bool anchor; - short offsetX,offsetY; // fUnits, only if !anchor - short parent,child; // only if anchor - bool identity; - ShortFract transform[2][2]; // 2.14 -} TrueTypeComponent; - -typedef struct { - unsigned short numComponents; - unsigned short useMyMetrics; // or INVALID_GLYPH_INDEX if nobody's metrics - TrueTypeComponent component[MAXNUMCOMPONENTS]; -} TrueTypeBluePrint; - -struct TrueTypeCompositeComponent { - unsigned short flags = 0; - unsigned short glyphIndex = 0; - short arg1 = 0; // or arg1 | arg2 - short arg2 = 0; - ShortFract xscale = 0; // or scale - ShortFract yscale = 0; - ShortFract scale01 = 0; - ShortFract scale10 = 0; -}; - -#define ONCURVE 0x01 - -class TrueTypeGlyph { // serves mainly as an interface to new user interface -public: - TrueTypeGlyph(void); - virtual ~TrueTypeGlyph(void); - LinkColor TheColor(short from, short to); // such that everybody can use this, not only the compiler - bool Misoriented(short contour); - - short xmin, ymin, xmax, ymax; // bounding box; xmin corresponds to left side bearing - short realLeftSideBearing, realRightSideBearing, blackBodyWidth; // as obtained from specifying left and right point by GrabHereInX, to do with auto-hinter??? - - // contour, knot data - int32_t numContoursInGlyph; - short startPoint[MAXCONTOURS]; - short endPoint[MAXCONTOURS]; - - short x[MAXPOINTS]; // these seem to be the (coordinates of the) control points - short y[MAXPOINTS]; // Use start|endPoint arrays for contour identification - bool onCurve[MAXPOINTS]; // on curve? - F26Dot6 xx[MAXPOINTS]; // used to get coordinates back from the rasterizer - F26Dot6 yy[MAXPOINTS]; - - // composite - bool composite,useMyMetrics; - short componentData[MAXCOMPONENTSIZE]; // binary of TT composite - short componentSize; // size of above data - short ComponentVersionNumber; // sort of a magic number, tends to be -1, I'd prefer this to disappear - TrueTypeBluePrint bluePrint; -private: - short dirChange[MAXPOINTS]; // used during TheColor -}; - -#define CO_CompInstrFollow 1 -#define CO_StdInstrFollow 2 -#define CO_NothingFollows 0 - -typedef struct { - short GlobalNON_OVERLAPPING, GlobalMORE_COMPONENTS, numberOfCompositeElements; - short anchorPrevPoints, nextExitOffset; - short GlobalUSEMYMETRICS; - short GlobalSCALEDCOMPONENTOFFSET; - short GlobalUNSCALEDCOMPONENTOFFSET; - short numberOfCompositeContours, numberOfCompositePoints; -} TTCompositeProfile; - -typedef enum { stripNothing = 0, stripSource, stripHints, stripBinary, stripEverything } StripCommand; - -typedef enum { asmGLYF = 0, asmPREP, asmFPGM } ASMType; -#define firstASMType asmGLYF -#define lastASMType asmFPGM -#define numASMTypes (lastASMType - firstASMType + 1) - -#define firstTTASMType asmGLYF -#define lastTTASMType asmFPGM -#define numTTASMTypes (lastTTASMType - firstTTASMType + 1) - - -class TrueTypeFont { -public: - TrueTypeFont(void); - bool Create(); - virtual ~TrueTypeFont(void); - void AssertMaxSfntSize(uint32_t minSfntSize, bool assertMainHandle, bool assertTempHandle); - void AssertMaxGlyphs(int32_t minGlyphs); - bool Read(File *file, TrueTypeGlyph *glyph, short *platformID, short *encodingID, wchar_t errMsg[]); - bool Write(File *file, wchar_t errMsg[]); - ControlValueTable *TheCvt(void); - bool GetCvt (TextBuffer *cvtText, wchar_t errMsg[]); - bool GetPrep(TextBuffer *prepText, wchar_t errMsg[]); - int32_t PrepBinSize(void); - bool GetFpgm(TextBuffer *fpgmText, wchar_t errMsg[]); - int32_t FpgmBinSize(void); - bool GetGlyf(int32_t glyphIndex, TextBuffer *glyfText, wchar_t errMsg[]); - bool GetTalk(int32_t glyphIndex, TextBuffer *talkText, wchar_t errMsg[]); - bool GetGlyph(int32_t glyphIndex, TrueTypeGlyph *glyph, wchar_t errMsg[]); - int32_t GlyfBinSize(void); - unsigned char* GlyfBin(void); - bool GetHMTXEntry(int32_t glyphIndex, int32_t *leftSideBearing, int32_t *advanceWidth); - int32_t NumberOfGlyphs(void); - int32_t GlyphIndexOf(uint32_t charCode); - bool GlyphIndecesOf(wchar_t textString[], int32_t maxNumGlyphIndeces, int32_t glyphIndeces[], int32_t *numGlyphIndeces, wchar_t errMsg[]); - uint32_t CharCodeOf(int32_t glyphIndex); - uint32_t AdjacentChar(uint32_t charCode, bool forward); - uint32_t FirstChar(); - CharGroup CharGroupOf(int32_t glyphIndex); - bool CMapExists(short platformID, short encodingID); - bool DefaultCMap(short *platformID, short *encodingID, wchar_t errMsg[]); - bool UnpackCMap(short platformID, short encodingID, wchar_t errMsg[]); - bool IsCvarTupleData(); - int32_t EstimatePrivateCvar(); - int32_t UpdatePrivateCvar(int32_t *size, unsigned char data[]); - bool HasPrivateCvar(); - bool GetPrivateCvar(TSICHeader &header); - bool MergePrivateCvarWithInstanceManager(const TSICHeader &header); - int32_t EstimateCvar(); - int32_t UpdateCvar(int32_t *size, unsigned char data[]); - void UpdateAdvanceWidthFlag(bool linear); - bool UpdateBinData(ASMType asmType, int32_t binSize, unsigned char *binData); - bool BuildNewSfnt(StripCommand strip, CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, - TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - wchar_t errMsg[]); - - bool InitIncrBuildSfnt(bool binaryOnly, wchar_t errMsg[]); - bool AddGlyphToNewSfnt(CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfBin, TextBuffer *glyfText, TextBuffer *talkText, wchar_t errMsg[]); - - bool TermIncrBuildSfnt(bool disposeOnly, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *fpgmText, wchar_t errMsg[]); - - void InitNewProfiles(void); - void InheritProfiles(void); - void UseNewProfiles(void); - sfnt_maxProfileTable GetProfile(void); - void UpdateGlyphProfile(TrueTypeGlyph *glyph); // used not only in BuildNewSfnt, but also in calculation of maxp and other odd places... - void UpdateAssemblerProfile(ASMType asmType, short maxFunctionDefs, short maxStackElements, short maxSizeOfInstructions); - void UpdateCompositeProfile(TrueTypeGlyph *glyph, TTCompositeProfile *compositeProfile, short context, short RoundingCode, short InstructionIndex, short *args, short argc, sfnt_glyphbbox *Newbbox, short *error); - bool GetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox); - int32_t GetUnitsPerEm(void); // FUnits Per EM (2048 is typical) - void UpdateAutohinterProfile(short maxElements, short maxTwilightPoints, short maxStorage); - bool HasSource(); - bool IsMakeTupleName(const std::wstring &name) const; - - unsigned char* GetSfntPtr(int32_t offset) - { - return (sfntHandle + offset); - } - - std::shared_ptr GetInstanceManager() - { - return instanceManager_; - } - - std::shared_ptr GetCvtVariationInterpolator() - { - return cvtVariationInterpolator_; - } - - template - bool GetDefaultCvts(std::vector & defaultCvts) - { - ControlValueTable *cvt = this->TheCvt(); - int32_t highestCvtNum = cvt->HighestCvtNum(); - defaultCvts.resize(highestCvtNum + 1, 0); - - // Get the default cvts. - for (int32_t i = 0; i <= highestCvtNum; i++) - { - short cvtValue; - if (cvt->GetCvtValue(i, &cvtValue)) - defaultCvts[i] = cvtValue; - } - - return true; - } - - bool ReverseInterpolateCvarTuples(); - - bool HasFvar() const - { - return fvar_.axisRecords.size() > 0; - } - - bool HasAvar() const - { - return avar_.axisSegmentMaps.size() > 0; - } - - bool IsVariationFont() const - { - return bVariationTypeface_; - } - - uint16_t GetVariationAxisCount() const - { - return axisCount_; - } - - std::shared_ptr > GetVariationAxisTags() - { - return variationAxisTags_; - } - -private: - void UpdateMetricProfile(TrueTypeGlyph *glyph); - bool SubGetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox); - bool TableExists(sfnt_TableTag tag); - int32_t GetTableOffset(sfnt_TableTag tag); - int32_t GetTableLength(sfnt_TableTag tag); - unsigned char *GetTablePointer(sfnt_TableTag tag); - bool UnpackHeadHheaMaxpHmtx(wchar_t errMsg[]); - bool UnpackGlitsLoca(wchar_t errMsg[]); - bool UpdateMaxPointsAndContours(wchar_t errMsg[]); - void EnterChar(int32_t glyphIndex, uint32_t charCode); - void SortGlyphMap(); - void GetFmt0(sfnt_mappingTable *map); - void GetFmt4(sfnt_mappingTable *map); - void GetFmt6(sfnt_mappingTable *map); - void GetFmt12(sfnt_mappingTable *map); - bool UnpackCharGroup(wchar_t errMsg[]); - bool GetSource(bool lowLevel, int32_t glyphIndex, TextBuffer *source, wchar_t errMsg[]); - bool GetTTOTable(sfnt_TableTag srcTag, TextBuffer *src, sfnt_TableTag binTag, ASMType asmType); - void CalculateNewCheckSums(void); - void CalculateCheckSumAdjustment(void); - void SortTableDirectory(void); - void PackMaxpHeadHhea(void); - uint32_t GetPackedGlyphsSizeEstimate(TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc); - uint32_t GetPackedGlyphSize(int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize); - uint32_t PackGlyphs(StripCommand strip, TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc, uint32_t *newIndexToLoc, unsigned char *dst); - uint32_t PackGlyph(unsigned char *dst, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfInstruction, sfnt_HorizontalMetrics *hmtx); - uint32_t StripGlyphBinary(unsigned char *dst, unsigned char *src, uint32_t srcLen); - uint32_t GetPackedGlyphSourceSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, int32_t glitIndex, sfnt_MemDataEntry *memGlit); - uint32_t GetPackedGlyphSourcesSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, sfnt_MemDataEntry *memGlit); - void PackGlyphSource(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, int32_t glitIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, - uint32_t *dstPos, unsigned char *dst); - void PackGlyphSources(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, - short type, int32_t glyphIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, - uint32_t *dstPos, unsigned char *dst); - bool GetNumPointsAndContours(int32_t glyphIndex, int32_t *numKnots, int32_t *numContours, int32_t *componentDepth); - bool IncrBuildNewSfnt(wchar_t errMsg[]); - bool SetSfnt(short platformID, short encodingID, wchar_t errMsg[]); - - void UnpackFvar(void); - void UnpackAvar(void); - - // sfnt - unsigned char *sfntHandle = nullptr; // handle to the sfnt file layout - uint32_t sfntSize = 0; // actual sfnt data size (in bytes) - uint32_t maxSfntSize = 0; // memory (in bytes) allocated for above handle - unsigned char *sfntTmpHandle = nullptr; // claudebe 1/26/94 temp sfnt Handle for use in BuildNewSfnt to avoid memory fragmentation - uint32_t maxTmpSfntSize = 0; // memory (in bytes) allocated for above handle - - sfnt_OffsetTable *offsetTable; // tmp for use in BuildNewSfnt to avoid memory fragmentation - sfnt_OffsetTable *tmpOffsetTable; // tmp for use in BuildNewSfnt to avoid memory fragmentation - - void *incrBuildSfntData; - - // 'cvt' - ControlValueTable *cvt = nullptr; - - // 'maxp' (maximum profile) table - sfnt_maxProfileTable profile; - sfnt_maxProfileTable newProfile; // used for 'maxp' computation - uint16 maxStackElements[numTTASMTypes]; // used for new heuristic in computing newProfile.maxStackElements - - // 'loca' (index to location) table - bool shortIndexToLocTable; // short or long loca table - bool outShortIndexToLocTable; // indicate if we want to write in long or short format - uint32_t *IndexToLoc; // modif to be able to convert the format loca table store in long format in the glyph informations rather than recomputing all the time - uint32_t *tmpIndexToLoc; // tmp for use in BuildNewSfnt to avoid memory fragmentation - int32_t numLocaEntries; - - // 'head', 'hhea' tables - FontMetricProfile metricProfile; - FontMetricProfile newMetricProfile; // used for 'maxp' computation - - //bool useIntegerScaling; - unsigned short macStyle; // passed from ReadHeader to WriteHeader as is - - - // 'GLIT' (glyph index) table - int32_t maxGlitEntries; - int32_t maxGlyphs; - sfnt_MemDataEntry *glit1; - int32_t glit1Entries; - sfnt_MemDataEntry *glit2; - int32_t glit2Entries; - - int32_t numberOfChars, numberOfGlyphs; // numberOfChars in *currently* unpacked cmap - uint32_t *charCodeOf; // glyph index - unsigned char *charGroupOf; // glyph index - - std::vector *glyphIndexMap; - - // TT Asm Tables - int32_t binSize[numASMTypes]; - unsigned char *binData[numASMTypes]; - - unsigned char *tmpFlags; // tmp for use in BuildNewSfnt to avoid memory fragmentation - - char *devMetricsPtr; - int32_t hdmxBinSize; - - char *ltshPtr; - int32_t ltshBinSize; - - char *vdmxPtr; - int32_t vdmxBinSize; - - // 'gasp' table - TtFont::GaspTable gaspTable; - int32_t gaspBinSize; - bool gaspLoaded = false; - - // 'TSIC' table - int32_t tsicBinSize; - // 'cvar' table - int32_t cvarBinSize; - bool tsicError = false; - - // vertical metrics: Quick & dirty "guess" for drawing the height lines in the main window. - int32_t unitsPerEm, // FUnits Per EM (2048 is typical) - capHeight, // glyph->ymax of 'H'... - xHeight, // glyph->ymax of 'x'... - descenderHeight; // glyph->ymin of 'p' - - // horizontal metrics: left side-bearing + bounding box width + right side-bearing = advance width (with the right side-bearing being the bound variable) - sfnt_HorizontalMetrics *horMetric; // this contains left side-bearing and advance width of all glyphs - - std::vector *postScriptNames = nullptr; - - std::shared_ptr cvtVariationInterpolator_ = nullptr; - - bool bVariationTypeface_ = false; - uint16_t axisCount_ = 0; - - FVarTableHeader fvar_; - AxisVariationHeader avar_; - - std::shared_ptr instanceManager_ = nullptr; - - std::shared_ptr> variationAxisTags_ = nullptr; // cache the axis tags -}; - -enum class CheckCompositeResult {Success, Tolerance, Fail}; -CheckCompositeResult CheckCompositeVariationCompatible(const short* pFirst, short firstSize, const short* pSecond, short secondSize); - - +/// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#ifndef TTFont_dot_h +#define TTFont_dot_h + +#define trialNumGlyphs 0x0400 +#define trialSfntSize 0x200000 // bytes + +#include +#include +#include +#include +#include +#include + +#include "opentypedefs.h" +#include "CvtManager.h" + +#define MAXCONTOURS 0x2000 +#define MAXPOINTS 0x4000 // rasterizer limit, needs extra two bits for line transition information in drop-out control algorithm + + //#define PHANTOMPOINTS 4 +#define PHANTOMPOINTS 2 + +#define LEFTSIDEBEARING 0 +#define RIGHTSIDEBEARING 1 + +//#define TOPSIDEBEARING 2 +//#define BOTTOMSIDEBEARING 3 + +#define INVALID_GLYPH_INDEX 0xffff + +#define maxNumCharCodes 0x10000 + +#define GASP_VERSION_DEFAULT 0x0000 +#define GASP_VERSION_CT_YAA 0x0001 +#define MAX_GASP_ENTRIES 6 +#define GASP_GRIDFIT 0x0001 +#define GASP_DOGRAY 0x0002 +#define GASP_CLEARTYPEGRIDFIT 0x0004 +#define GASP_DONONCTDIRAA 0x0008 + +namespace TtFont +{ + typedef struct { + unsigned short rangeMaxPPEM; + unsigned short rangeGaspBehavior; + } GaspRange; + + typedef struct { + unsigned short version; + unsigned short numRanges; + std::vector gaspRange; + } GaspTable; + + typedef struct { + unsigned short version; + unsigned short numRanges; + GaspRange gaspRange[1]; + } GaspTableRaw; +} + +typedef struct { + short xmin, ymin, xmax, ymax; +} sfnt_glyphbbox; + +typedef struct { + unsigned short CharCode; + CharGroup type; +} GlyphGroupType; + +// 'glit' (glyph index) table +typedef struct { + unsigned short glyphCode; + unsigned short length; + int32_t offset; +} sfnt_FileDataEntry; + +typedef struct { + unsigned short glyphCode; + uint32_t length; + int32_t offset; +} sfnt_MemDataEntry; +/****** what we have here are two pairs of private tables TS00 through TS03, the first pair for TT sources, +the second pair for TMT sources. Within each pair, the first is merely an index into the second, i.e. TT of +glyph n starts at TS01[TS00[n].offset] and TMT of glyph n starts at TS03[TS02[n].offset], assuming that we +are talking byte offsets here, and TS01 and TS03 are just arrays of bytes. The fact that we have two dif- +ferent structs here could be hidden in the sfnt module: in the font files we may have a short form of the +'glit' table, wherein all the offsets are short (sfnt_FileDataEntry), restricting the size, but in the rest +of the application we want to work only with the long offsets (sfnt_MemDataEntry) *****/ + +typedef struct { + short xMin, yMin, xMax, yMax; // rectilinear convex hull of all glyphs (union of all glyphs' bounding boxes) + short advanceWidthMax; + short minLeftSideBearing; + short minRightSideBearing; + short xMaxExtent; +} FontMetricProfile; + +struct VariationAxisRecord +{ + uint32_t axisTag; + Fixed16_16 minValue; + Fixed16_16 defaultValue; + Fixed16_16 maxValue; + uint16_t flags; + uint16_t nameID; +}; + +struct VariationInstanceRecord +{ + uint16_t nameID; + uint16_t flags; + std::vector< Fixed16_16> coordinates; + //uint16_t postScriptNameID; +}; + +struct FVarTableHeader +{ + uint16_t majorVersion; + uint16_t minorVersion; + uint16_t offsetToAxesArray; + uint16_t countSizePairs; + uint16_t axisCount; + uint16_t axisSize; + uint16_t instanceCount; + uint16_t instanceSize; + std::vector axisRecords; + std::vector< VariationInstanceRecord> instanceRecords; +}; + +struct ShortFracCorrespondence +{ + Fixed2_14 fromCoord; //A normalized coordinate value obtained using default normalization. + Fixed2_14 toCoord; //The modified, normalized coordinate value. +}; + +struct ShortFracSegment +{ + uint16_t pairCount; // The number of correspondence pairs for this axis. + std::vector axisValueMaps; // The array of axis value map records for this axis. +}; + +struct AxisVariationHeader +{ + uint16_t majorVersion; + uint16_t minorVersion; + uint16_t reserved; + uint16_t axisCount; + std::vector axisSegmentMaps; // The segment maps array - one segment map for each axis +}; + +typedef struct { + uint16_t majorVersion; + uint16_t minorVersion; + uint16_t tupleVariationCount; + uint16_t offsetToData; +} CvarHeader; + +struct TSICRecord { + uint16_t flags = 0; + uint16_t numCVTEntries = 0; + uint16_t nameLength = 0; + std::wstring name; // uint16_t NameArray[nameLength]; + std::vector cvts; // uint16_t CVTArray[numCVTEntries]; + std::vector cvtValues; // FWORD CVTValueArray[numCVTEntries]; +}; + +struct TSICHeader { + uint16_t majorVersion = 0; + uint16_t minorVersion = 0; + uint16_t flags = 0; + uint16_t axisCount = 0; + uint16_t recordCount = 0; + uint16_t reserved = 0; + std::vector axes; //uint32_t AxisArray[axisCount]; + std::vector> locations; //uint16_t RecordLocations[recordCount][axisCount]; + std::vector tsicRecords; //TSICRecord TSICRecord[recordCount]; +}; + +typedef struct { + uint16_t cvt; + int16_t delta; +} CvtElement; + +#define MAXCVT maxCvtNum + +#define MAXBINSIZE (0x10000L - 4) // don't push the limit + +#define MAXCOMPONENTSIZE 256 +#define MAXNUMCOMPONENTS (MAXCOMPONENTSIZE/3) // flags, glyphIndex, pair of bytes for offset = minimal size used by each component + +// these values to assert 'maxp' ok with templates (possibly generated by auto-hinter) +#define MIN_STORAGE 47 +#define MIN_TWILIGHTPOINTS 16 +#define MIN_ELEMENTS 2 + +// This is the magic cookie for the preferences. +#define MAGIC 0xA2340987 // note that this is a different magic number than SFNT_MAGIC... + +typedef struct { + uint32_t unicode; + unsigned short glyphIndex; +} UniGlyphMap; + +typedef struct { + uint32_t unicode; + unsigned short glyphIndex; + unsigned short numContours; + bool rounded; + bool anchor; + short offsetX,offsetY; // fUnits, only if !anchor + short parent,child; // only if anchor + bool identity; + ShortFract transform[2][2]; // 2.14 +} TrueTypeComponent; + +typedef struct { + unsigned short numComponents; + unsigned short useMyMetrics; // or INVALID_GLYPH_INDEX if nobody's metrics + TrueTypeComponent component[MAXNUMCOMPONENTS]; +} TrueTypeBluePrint; + +struct TrueTypeCompositeComponent { + unsigned short flags = 0; + unsigned short glyphIndex = 0; + short arg1 = 0; // or arg1 | arg2 + short arg2 = 0; + ShortFract xscale = 0; // or scale + ShortFract yscale = 0; + ShortFract scale01 = 0; + ShortFract scale10 = 0; +}; + +#define ONCURVE 0x01 + +class TrueTypeGlyph { // serves mainly as an interface to new user interface +public: + TrueTypeGlyph(void); + virtual ~TrueTypeGlyph(void); + LinkColor TheColor(short from, short to); // such that everybody can use this, not only the compiler + bool Misoriented(short contour); + + short xmin, ymin, xmax, ymax; // bounding box; xmin corresponds to left side bearing + short realLeftSideBearing, realRightSideBearing, blackBodyWidth; // as obtained from specifying left and right point by GrabHereInX, to do with auto-hinter??? + + // contour, knot data + int32_t numContoursInGlyph; + short startPoint[MAXCONTOURS]; + short endPoint[MAXCONTOURS]; + + short x[MAXPOINTS]; // these seem to be the (coordinates of the) control points + short y[MAXPOINTS]; // Use start|endPoint arrays for contour identification + bool onCurve[MAXPOINTS]; // on curve? + F26Dot6 xx[MAXPOINTS]; // used to get coordinates back from the rasterizer + F26Dot6 yy[MAXPOINTS]; + + // composite + bool composite,useMyMetrics; + short componentData[MAXCOMPONENTSIZE]; // binary of TT composite + short componentSize; // size of above data + short ComponentVersionNumber; // sort of a magic number, tends to be -1, I'd prefer this to disappear + TrueTypeBluePrint bluePrint; +private: + short dirChange[MAXPOINTS]; // used during TheColor +}; + +#define CO_CompInstrFollow 1 +#define CO_StdInstrFollow 2 +#define CO_NothingFollows 0 + +typedef struct { + short GlobalNON_OVERLAPPING, GlobalMORE_COMPONENTS, numberOfCompositeElements; + short anchorPrevPoints, nextExitOffset; + short GlobalUSEMYMETRICS; + short GlobalSCALEDCOMPONENTOFFSET; + short GlobalUNSCALEDCOMPONENTOFFSET; + short numberOfCompositeContours, numberOfCompositePoints; +} TTCompositeProfile; + +typedef enum { stripNothing = 0, stripSource, stripHints, stripBinary, stripEverything } StripCommand; + +typedef enum { asmGLYF = 0, asmPREP, asmFPGM } ASMType; +#define firstASMType asmGLYF +#define lastASMType asmFPGM +#define numASMTypes (lastASMType - firstASMType + 1) + +#define firstTTASMType asmGLYF +#define lastTTASMType asmFPGM +#define numTTASMTypes (lastTTASMType - firstTTASMType + 1) + + +class TrueTypeFont { +public: + TrueTypeFont(void); + bool Create(); + virtual ~TrueTypeFont(void); + void AssertMaxSfntSize(uint32_t minSfntSize, bool assertMainHandle, bool assertTempHandle); + void AssertMaxGlyphs(int32_t minGlyphs); + bool Read(File *file, TrueTypeGlyph *glyph, short *platformID, short *encodingID, wchar_t errMsg[], size_t errMsgLen); + bool Write(File *file, wchar_t errMsg[], size_t errMsgLen); + ControlValueTable *TheCvt(void); + bool GetCvt (TextBuffer *cvtText, wchar_t errMsg[], size_t errMsgLen); + bool GetPrep(TextBuffer *prepText, wchar_t errMsg[], size_t errMsgLen); + int32_t PrepBinSize(void); + bool GetFpgm(TextBuffer *fpgmText, wchar_t errMsg[], size_t errMsgLen); + int32_t FpgmBinSize(void); + bool GetGlyf(int32_t glyphIndex, TextBuffer *glyfText, wchar_t errMsg[], size_t errMsgLen); + bool GetTalk(int32_t glyphIndex, TextBuffer *talkText, wchar_t errMsg[], size_t errMsgLen); + bool GetGlyph(int32_t glyphIndex, TrueTypeGlyph *glyph, wchar_t errMsg[], size_t errMsgLen); + int32_t GlyfBinSize(void); + unsigned char* GlyfBin(void); + bool GetHMTXEntry(int32_t glyphIndex, int32_t *leftSideBearing, int32_t *advanceWidth); + int32_t NumberOfGlyphs(void); + int32_t GlyphIndexOf(uint32_t charCode); + bool GlyphIndecesOf(wchar_t textString[], int32_t maxNumGlyphIndeces, int32_t glyphIndeces[], int32_t *numGlyphIndeces, wchar_t errMsg[], size_t errMsgLen); + uint32_t CharCodeOf(int32_t glyphIndex); + uint32_t AdjacentChar(uint32_t charCode, bool forward); + uint32_t FirstChar(); + CharGroup CharGroupOf(int32_t glyphIndex); + bool CMapExists(short platformID, short encodingID); + bool DefaultCMap(short *platformID, short *encodingID, wchar_t errMsg[], size_t errMsgLen); + bool UnpackCMap(short platformID, short encodingID, wchar_t errMsg[], size_t errMsgLen); + bool IsCvarTupleData(); + int32_t EstimatePrivateCvar(); + int32_t UpdatePrivateCvar(int32_t *size, unsigned char data[]); + bool HasPrivateCvar(); + bool GetPrivateCvar(TSICHeader &header); + bool MergePrivateCvarWithInstanceManager(const TSICHeader &header); + int32_t EstimateCvar(); + int32_t UpdateCvar(int32_t *size, unsigned char data[]); + void UpdateAdvanceWidthFlag(bool linear); + bool UpdateBinData(ASMType asmType, int32_t binSize, unsigned char *binData); + bool BuildNewSfnt(StripCommand strip, CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, + TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + wchar_t errMsg[], size_t errMsgLen); + + bool InitIncrBuildSfnt(bool binaryOnly, wchar_t errMsg[], size_t errMsgLen); + bool AddGlyphToNewSfnt(CharGroup group, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfBin, TextBuffer *glyfText, TextBuffer *talkText, wchar_t errMsg[], size_t errMsgLen); + + bool TermIncrBuildSfnt(bool disposeOnly, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *fpgmText, wchar_t errMsg[], size_t errMsgLen); + + void InitNewProfiles(void); + void InheritProfiles(void); + void UseNewProfiles(void); + sfnt_maxProfileTable GetProfile(void); + void UpdateGlyphProfile(TrueTypeGlyph *glyph); // used not only in BuildNewSfnt, but also in calculation of maxp and other odd places... + void UpdateAssemblerProfile(ASMType asmType, short maxFunctionDefs, short maxStackElements, short maxSizeOfInstructions); + void UpdateCompositeProfile(TrueTypeGlyph *glyph, TTCompositeProfile *compositeProfile, short context, short RoundingCode, short InstructionIndex, short *args, short argc, sfnt_glyphbbox *Newbbox, short *error); + bool GetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox); + int32_t GetUnitsPerEm(void); // FUnits Per EM (2048 is typical) + void UpdateAutohinterProfile(short maxElements, short maxTwilightPoints, short maxStorage); + bool HasSource(); + bool IsMakeTupleName(const std::wstring &name) const; + + unsigned char* GetSfntPtr(int32_t offset) + { + return (sfntHandle + offset); + } + + std::shared_ptr GetInstanceManager() + { + return instanceManager_; + } + + std::shared_ptr GetCvtVariationInterpolator() + { + return cvtVariationInterpolator_; + } + + template + bool GetDefaultCvts(std::vector & defaultCvts) + { + ControlValueTable *cvt = this->TheCvt(); + int32_t highestCvtNum = cvt->HighestCvtNum(); + defaultCvts.resize(highestCvtNum + 1, 0); + + // Get the default cvts. + for (int32_t i = 0; i <= highestCvtNum; i++) + { + short cvtValue; + if (cvt->GetCvtValue(i, &cvtValue)) + defaultCvts[i] = cvtValue; + } + + return true; + } + + bool ReverseInterpolateCvarTuples(); + + bool HasFvar() const + { + return fvar_.axisRecords.size() > 0; + } + + bool HasAvar() const + { + return avar_.axisSegmentMaps.size() > 0; + } + + bool IsVariationFont() const + { + return bVariationTypeface_; + } + + uint16_t GetVariationAxisCount() const + { + return axisCount_; + } + + std::shared_ptr > GetVariationAxisTags() + { + return variationAxisTags_; + } + +private: + void UpdateMetricProfile(TrueTypeGlyph *glyph); + bool SubGetNumberOfPointsAndContours(int32_t glyphIndex, short *contours, short *points, short *ComponentDepth, sfnt_glyphbbox *bbox); + bool TableExists(sfnt_TableTag tag); + int32_t GetTableOffset(sfnt_TableTag tag); + int32_t GetTableLength(sfnt_TableTag tag); + unsigned char *GetTablePointer(sfnt_TableTag tag); + bool UnpackHeadHheaMaxpHmtx(wchar_t errMsg[], size_t errMsgLen); + bool UnpackGlitsLoca(wchar_t errMsg[], size_t errMsgLen); + bool UpdateMaxPointsAndContours(wchar_t errMsg[], size_t errMsgLen); + void EnterChar(int32_t glyphIndex, uint32_t charCode); + void SortGlyphMap(); + void GetFmt0(sfnt_mappingTable *map); + void GetFmt4(sfnt_mappingTable *map); + void GetFmt6(sfnt_mappingTable *map); + void GetFmt12(sfnt_mappingTable *map); + bool UnpackCharGroup(wchar_t errMsg[], size_t errMsgLen); + bool GetSource(bool lowLevel, int32_t glyphIndex, TextBuffer *source, wchar_t errMsg[], size_t errMsgLen); + bool GetTTOTable(sfnt_TableTag srcTag, TextBuffer *src, sfnt_TableTag binTag, ASMType asmType); + void CalculateNewCheckSums(void); + void CalculateCheckSumAdjustment(void); + void SortTableDirectory(void); + void PackMaxpHeadHhea(void); + uint32_t GetPackedGlyphsSizeEstimate(TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc); + uint32_t GetPackedGlyphSize(int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize); + uint32_t PackGlyphs(StripCommand strip, TrueTypeGlyph *glyph, int32_t glyphIndex, uint32_t *oldIndexToLoc, uint32_t *newIndexToLoc, unsigned char *dst); + uint32_t PackGlyph(unsigned char *dst, int32_t glyphIndex, TrueTypeGlyph *glyph, int32_t glyfBinSize, unsigned char *glyfInstruction, sfnt_HorizontalMetrics *hmtx); + uint32_t StripGlyphBinary(unsigned char *dst, unsigned char *src, uint32_t srcLen); + uint32_t GetPackedGlyphSourceSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, int32_t glitIndex, sfnt_MemDataEntry *memGlit); + uint32_t GetPackedGlyphSourcesSize(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, sfnt_MemDataEntry *memGlit); + void PackGlyphSource(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, int32_t glitIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, + uint32_t *dstPos, unsigned char *dst); + void PackGlyphSources(TextBuffer *glyfText, TextBuffer *prepText, TextBuffer *cvtText, TextBuffer *talkText, TextBuffer *fpgmText, + short type, int32_t glyphIndex, sfnt_FileDataEntry *fileGlit, sfnt_MemDataEntry *memGlit, + uint32_t *dstPos, unsigned char *dst); + bool GetNumPointsAndContours(int32_t glyphIndex, int32_t *numKnots, int32_t *numContours, int32_t *componentDepth); + bool IncrBuildNewSfnt(wchar_t errMsg[], size_t errMsgLen); + bool SetSfnt(short platformID, short encodingID, wchar_t errMsg[], size_t errMsgLen); + + void UnpackFvar(void); + void UnpackAvar(void); + + // sfnt + unsigned char *sfntHandle = nullptr; // handle to the sfnt file layout + uint32_t sfntSize = 0; // actual sfnt data size (in bytes) + uint32_t maxSfntSize = 0; // memory (in bytes) allocated for above handle + unsigned char *sfntTmpHandle = nullptr; // claudebe 1/26/94 temp sfnt Handle for use in BuildNewSfnt to avoid memory fragmentation + uint32_t maxTmpSfntSize = 0; // memory (in bytes) allocated for above handle + + sfnt_OffsetTable *offsetTable; // tmp for use in BuildNewSfnt to avoid memory fragmentation + sfnt_OffsetTable *tmpOffsetTable; // tmp for use in BuildNewSfnt to avoid memory fragmentation + + void *incrBuildSfntData; + + // 'cvt' + ControlValueTable *cvt = nullptr; + + // 'maxp' (maximum profile) table + sfnt_maxProfileTable profile; + sfnt_maxProfileTable newProfile; // used for 'maxp' computation + uint16 maxStackElements[numTTASMTypes]; // used for new heuristic in computing newProfile.maxStackElements + + // 'loca' (index to location) table + bool shortIndexToLocTable; // short or long loca table + bool outShortIndexToLocTable; // indicate if we want to write in long or short format + uint32_t *IndexToLoc; // modif to be able to convert the format loca table store in long format in the glyph informations rather than recomputing all the time + uint32_t *tmpIndexToLoc; // tmp for use in BuildNewSfnt to avoid memory fragmentation + int32_t numLocaEntries; + + // 'head', 'hhea' tables + FontMetricProfile metricProfile; + FontMetricProfile newMetricProfile; // used for 'maxp' computation + + //bool useIntegerScaling; + unsigned short macStyle; // passed from ReadHeader to WriteHeader as is + + + // 'GLIT' (glyph index) table + int32_t maxGlitEntries; + int32_t maxGlyphs; + sfnt_MemDataEntry *glit1; + int32_t glit1Entries; + sfnt_MemDataEntry *glit2; + int32_t glit2Entries; + + int32_t numberOfChars, numberOfGlyphs; // numberOfChars in *currently* unpacked cmap + uint32_t *charCodeOf; // glyph index + unsigned char *charGroupOf; // glyph index + + std::vector *glyphIndexMap; + + // TT Asm Tables + int32_t binSize[numASMTypes]; + unsigned char *binData[numASMTypes]; + + unsigned char *tmpFlags; // tmp for use in BuildNewSfnt to avoid memory fragmentation + + char *devMetricsPtr; + int32_t hdmxBinSize; + + char *ltshPtr; + int32_t ltshBinSize; + + char *vdmxPtr; + int32_t vdmxBinSize; + + // 'gasp' table + TtFont::GaspTable gaspTable; + int32_t gaspBinSize; + bool gaspLoaded = false; + + // 'TSIC' table + int32_t tsicBinSize; + // 'cvar' table + int32_t cvarBinSize; + bool tsicError = false; + + // vertical metrics: Quick & dirty "guess" for drawing the height lines in the main window. + int32_t unitsPerEm, // FUnits Per EM (2048 is typical) + capHeight, // glyph->ymax of 'H'... + xHeight, // glyph->ymax of 'x'... + descenderHeight; // glyph->ymin of 'p' + + // horizontal metrics: left side-bearing + bounding box width + right side-bearing = advance width (with the right side-bearing being the bound variable) + sfnt_HorizontalMetrics *horMetric; // this contains left side-bearing and advance width of all glyphs + + std::vector *postScriptNames = nullptr; + + std::shared_ptr cvtVariationInterpolator_ = nullptr; + + bool bVariationTypeface_ = false; + uint16_t axisCount_ = 0; + + FVarTableHeader fvar_; + AxisVariationHeader avar_; + + std::shared_ptr instanceManager_ = nullptr; + + std::shared_ptr> variationAxisTags_ = nullptr; // cache the axis tags +}; + +enum class CheckCompositeResult {Success, Tolerance, Fail}; +CheckCompositeResult CheckCompositeVariationCompatible(const short* pFirst, short firstSize, const short* pSecond, short secondSize); + + #endif // TTFont_dot_h \ No newline at end of file diff --git a/src/TTGenerator.cpp b/src/TTGenerator.cpp index 7968452..6b5d104 100644 --- a/src/TTGenerator.cpp +++ b/src/TTGenerator.cpp @@ -1,2601 +1,2600 @@ -/***** - - TTGenerator.c - New TypeMan Talk Compiler - Code Generators - - Copyright (c) Microsoft Corporation. - Licensed under the MIT License. - -*****/ -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include -#include -#include "pch.h" - -//#define maxBitsKnot 12 /* depends on MAXPOINTS, which is (currently) 2048... */ -//#define knotMask 4095L -//#define maxBitsCvt 16 /* cvt numbers seem to be 16bit-integers */ -//#define cvtMask 65535L /* allow for -1 */ -#define strokeFudge STRAIGHTANGLEFUDGE /* = 1.5 degrees */ -#define serifFudge 5 /* degrees */ -#define diagFudge 5 /* degrees */ -#define diagFudgeMT 5 /* degrees */ -#define noJunction 25 /* degrees */ -#define gmndItFudge 12 -#define neighFudge 7.5 /* degrees */ -#define vacuFudge(font) ((this)->emHeight/200) - -#define LSBTMP (FIRSTTMPCVT + 3) -#define RSBTMP (FIRSTTMPCVT + 4) -#define RESERVED_HEIGHTSPACE_START 36 -#define ALIGN_TOLERANCE 0.5 // degrees -#define MAXSTROKESIDEANGLEDIFFERENCE 5.0 /* degrees */ -#define ND_HEIGHT_STORE_1 23 -#define ND_ITALIC_STORE_1 43 -#define MAXIMUM_ITALIC_ANGLE_DEVIATION 3.5 -#define MAIN_STROKE_IS_ONE_PIXEL_STORAGE 8 /* boolean */ - -#define maxVacuForms 16L - -typedef struct { - Rounding round[2]; // xRomanDir, yRomanDir - short cvt; // YAnchor's cvt for IStroke optimization - bool touched[2]; // xRomanDir, yRomanDir, have to keep track of for FixDStrokes (?) - bool dStroke; // part of a Stroke which is neither diagonal nor horizontal - bool iStroke; // part of a Stroke which is sufficiently close to the main stroke angle - bool on; // on-curve point - bool vacu; // temp use in VacuFormRound - short deltaAngle; // (rounded) degrees, left turn => positive, right turn => negative, straight: 0 -} Attribute; -typedef Attribute* AttributePtr; - -typedef struct { - short type; - short radius; - short cvt; - bool forward[2]; - short knot[4]; -} VacuFormParams; - -typedef Vector* VectorPtr; - -const Rounding deltaRounding[roff-rthg+1][roff-rthg+1] = {{rtg, rtdg, rthg, roff, roff, roff}, - {roff, rtdg, roff, roff, roff, roff}, - {rthg, rtdg, rtg, rdtg, rutg, roff}, - {rthg, rtdg, rtg, rdtg, rutg, roff}, - {rthg, rtdg, rtg, rdtg, rutg, roff}, - {roff, roff, roff, roff, roff, roff}}; - -const F26Dot6 defaultMinDistAmount[roff-rthg+1][roff-rthg+1] = {{one6, half6, half6, half6, half6, 0}, - {half6, half6, half6, half6, half6, 0}, - {half6, half6, one6, 0, one6, 0}, - {half6, half6, one6, 0, one6, 0}, - {half6, half6, one6, 0, one6, 0}, - {0, 0, 0, 0, 0, 0}}; - -void TTGenerator::MainStrokeAngle(short angle100, wchar_t error[]) { /* abstract */ } -void TTGenerator::GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[]) { /* abstract */ } -void TTGenerator::SetRounding(bool y, Rounding round, short params, short param[]) { /* abstract */ } -void TTGenerator::SetItalicStroke(bool phase, wchar_t error[]) { /* abstract */ } -void TTGenerator::Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[]) { /* abstract */ } -void TTGenerator::GrabHereInX(short left, short right, wchar_t error[]) { /* abstract */ } -void TTGenerator::Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[]) { /* abstract */ } -void TTGenerator::Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[]) { /* abstract */ } -void TTGenerator::BeginCodePath(short fpgmBias, wchar_t error[]) { /* abstract */ } -void TTGenerator::EndCodePath(wchar_t error[]) { /* abstract */ } -void TTGenerator::ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[]) { /* abstract */ } -void TTGenerator::ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[]) { /* abstract */ } -void TTGenerator::ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[]) { /* abstract */ } -void TTGenerator::ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[]) { /* abstract */ } -void TTGenerator::ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[]) { /* abstract */ } -void TTGenerator::ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]) { /* abstract */ } -void TTGenerator::ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]) { /* abstract */ } -void TTGenerator::ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]) { /* abstract */ } -void TTGenerator::Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[]) { /* abstract */ } -void TTGenerator::Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[]) { /* abstract */ } -void TTGenerator::Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[]) { /* abstract */ } -void TTGenerator::Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[]) { /* abstract */ } -void TTGenerator::Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[]) { /* abstract */ } -void TTGenerator::DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[]) { /* abstract */ } -void TTGenerator::IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[]) { /* abstract */ } -void TTGenerator::FixDStrokes(void) { /* abstract */ } -void TTGenerator::Serif(bool forward, short type, short knots, short knot[], wchar_t error[]) { /* abstract */ } -void TTGenerator::Scoop(short parent0, short child, short parent1, wchar_t error[]) { /* abstract */ } -void TTGenerator::Smooth(short y, short italicFlag) { /* abstract */ } -void TTGenerator::Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[]) { /* abstract */ } -void TTGenerator::VacuFormLimit(short ppem) { /* abstract */ } -void TTGenerator::VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[]) { /* abstract */ } -void TTGenerator::Call(short actParams, short anyNum[], short functNum) { /* abstract */ } -void TTGenerator::Asm(bool inLine, wchar_t text[], wchar_t error[]) { /* abstract */ } -void TTGenerator::Quit(void) { /* abstract */ } -void TTGenerator::InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError) { /* abstract */ } -void TTGenerator::TermTTGenerator(void) { /* abstract */ } -TTGenerator::TTGenerator(void) { /* abstract */ } -TTGenerator::~TTGenerator(void) { /* abstract */ } - -class DiagParam : public ListElem { -public: - DiagParam(void); - virtual ~DiagParam(void); - bool leftStationary[2]; - short knot[4]; -}; - -class AlignParam : public ListElem { -public: - AlignParam(void); - virtual ~AlignParam(void); - short parent0,parent1,children,child[maxParams]; -}; - -DiagParam::DiagParam(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // DiagParam::DiagParam - -DiagParam::~DiagParam(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // DiagParam::~DiagParam - -AlignParam::AlignParam(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // AlignParam::AlignParam - -AlignParam::~AlignParam(void) { - // nix, but have to have at least a (pair of) method(s) or else the compiler complains... -} // AlignParam::~AlignParam - -typedef enum {fvOnX, fvOnY, fvOnPV, fvOnLine} FVMTDirection; - -class TTSourceGenerator : public TTGenerator { -public: - virtual void MainStrokeAngle(short angle100, wchar_t error[]); - virtual void GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[]); - virtual void SetRounding(bool y, Rounding round, short params, short param[]); - virtual void SetItalicStroke(bool phase, wchar_t error[]); - virtual void Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[]); - virtual void GrabHereInX(short left, short right, wchar_t error[]); - virtual void Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[]); - virtual void Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[]); - virtual void BeginCodePath(short fpgmBias, wchar_t error[]); - virtual void EndCodePath(wchar_t error[]); - virtual void ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[]); - virtual void ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[]); - virtual void ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[]); - virtual void ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[]); - virtual void ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[]); - virtual void ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]); - virtual void ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]); - virtual void ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]); - virtual void Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[]); - virtual void Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[]); - virtual void Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[]); - virtual void Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[]); - virtual void Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[]); - virtual void DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[]); - virtual void IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[]); - virtual void FixDStrokes(void); - virtual void Serif(bool forward, short type, short knots, short knot[], wchar_t error[]); - virtual void Scoop(short parent0, short child, short parent1, wchar_t error[]); - virtual void Smooth(short y, short italicFlag); - virtual void VacuFormLimit(short ppem); - virtual void VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[]); - virtual void Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[]); - virtual void Call(short actParams, short anyNum[], short functNum); - virtual void Asm(bool inLine, wchar_t text[], wchar_t error[]); - virtual void Quit(void); - virtual void InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError); - virtual void InitCodePathState(void); - virtual void TermCodePathState(void); - virtual void TermTTGenerator(void); - TTSourceGenerator(void); - virtual ~TTSourceGenerator(void); -private: - ProjFreeVector xRomanPV,yRomanPV,xItalPV,yItalPV,xAdjItalPV,yAdjItalPV; // generalized projection freedom vectors used as actual parameters in frequent standard cases - RVector xAxis,yAxis,slope; // of italics, normalised - short riseCvt,runCvt; // for GlyphStrokeAngle - double cosF,tanF; // of strokeFudge - double cosF0; // of MAXSTROKESIDEANGLEDIFFERENCE - double cosF1; // of ALIGN_TOLERANCE - double cosF2,sinF22; // of MAXIMUM_ITALIC_ANGLE_DEVIATION, 2*MAXIMUM_ITALIC_ANGLE_DEVIATION - double sinF3; // of serifFudge - double tanF4; // of diagFudge - double cosF5; // of gmndItFudge - double cosF6; // of neighFudge - double cosMT; // of diagFudgeMT - double tanMT; // of diagFudgeMT - bool italic; // an italic font with mainStrokeAngle other than 90 degree ±STRAIGHTANGLEFUDGE - bool mainStrokeAngle,glyphStrokeAngle,setItalicStrokePhase,setItalicStrokeAngle; - bool xSmooth,ySmooth; - short leftAnchor,rightAnchor; // for GrabHereInX - TrueTypeFont *font; - TrueTypeGlyph *glyph; - int32_t glyphIndex,charCode,emHeight; - CharGroup charGroup; // mapped from current glyph - short knots; - AttributePtr attrib; // allocate just as many as we need - VectorPtr V; // here, too - LinearListStruct *diagonals; - LinearListStruct *aligns; - short vacuFormLimit; // ppem size at which vacuforming is turned off - short vacuForms; - VacuFormParams vacuForm[maxVacuForms]; // but not here - bool legacyCompile; - - TTEngine *tt; // if no TTEngine is used, then assume we are building up the cvt - - bool ItalicAngleAllowed(ProjFreeVector *projFreeVector, wchar_t error[]); - RVector MakeRVector(const TTVectorDesc *ttv, bool pv); - bool AlmostPerpendicular(const TTVectorDesc *pv, const TTVectorDesc *fv, wchar_t error[]); - void AssertFreeProjVector(const TTVectorDesc *pv, const TTVectorDesc *fv); - short ProjectedDistance(bool signedDistance, short parent, short child, ProjFreeVector *projFreeVector); - void AssertPositiveProjectedDistance(short *parent, short *child, ProjFreeVector *projFreeVector); - void CondRoundInterpolees(bool y, short children, short child[], Rounding actual[], Rounding targeted); // used only once, why a method, then? - void AssertStrokePhaseAngle(FVOverride fv, bool leftStationary[], short knot[]); - short Neighbour(short parent0, short parent1, short child, bool immediate); - void AssertVectorsAtPotentialJunction(TTVector pv, short parent0, short parent1, short child); - FVMTDirection CalcDiagonalFVMT(FVOverride fv, short parent0, short parent1, short child, RVector strokeDirection, short *refPoint0, short *refPoint1); - FVMTDirection CalcAlignFVMT(FVOverride fv, short parent0, short parent1, short child, RVector alignDirection, short *refPoint0, short *refPoint1); - void AssertFVMT(FVMTDirection fvmt, short point0, short point1); - void Touched(short knot, TTVDirection dir); - short TheCvt(short parent, short child, LinkColor color, LinkDirection direction, CvtCategory category, short distance); // color, category, and distance: -1 or invalidCvtNum for default - void DoVacuFormRound(void); -}; - -short Next(short knot, short base, short n, short delta) { - return (knot - base + delta)%n + base; -} // Next - -bool SameVectorsForAllChildren(ProjFreeVector *projFreeVector, int32_t children) { - int32_t i; - TTVectorDesc fv; - - if (children <= 1) return true; - - // we only have to look at the freedom vectors, since there is only one projection vector to begin with - fv = projFreeVector->fv[0]; - for (i = 1; i < children && projFreeVector->fv[i].dir == fv.dir && projFreeVector->fv[i].from == fv.from && projFreeVector->fv[i].to == fv.to; i++); - - return i == children; -} // SameVectorsForAllChildren - -void TTSourceGenerator::MainStrokeAngle(short angle100, wchar_t error[]) { - double deg = (double)angle100/100.0,rad = Rad(deg); - - this->slope.x = 0.0; - this->slope.y = 1.0; - this->italic = false; - - if (this->mainStrokeAngle) { - swprintf(error,L"cannot use MAINSTROKEANGLE more than once per glyph, or together with GLYPHSTROKEANGLE"); - } else { - this->mainStrokeAngle = this->glyphStrokeAngle = true; - if (deg < 90.0 - strokeFudge || 90.0 + strokeFudge < deg) { - this->slope.x = cos(rad); - this->slope.y = sin(rad); - this->italic = true; - } - } -} /* TTSourceGenerator::MainStrokeAngle */ - -void TTSourceGenerator::GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[]) { - short riseCvtValue,runCvtValue; - short deg; - double rad; - - if (this->glyphStrokeAngle) - swprintf(error,L"cannot use GLYPHSTROKEANGLE more than once per glyph, or together with MAINSTROKEANGLE"); - else { - this->font->TheCvt()->GetCvtValue(riseCvt,&riseCvtValue); - this->font->TheCvt()->GetCvtValue(runCvt, &runCvtValue); - if (!riseCvtValue) { swprintf(error,L"Cvt value of italic rise %hi cannot be 0",riseCvt); return; } - if (!runCvtValue) { swprintf(error,L"Cvt value of italic run %hi cannot be 0",runCvt); return; } - this->riseCvt = riseCvt; - this->runCvt = runCvt; - rad = atan((double)riseCvtValue/(double)runCvtValue); deg = (short)Round(100*Deg(rad)); - this->MainStrokeAngle(deg,error); - this->tt->CALL88(riseCvt,runCvt); - } -} // TTSourceGenerator::GlyphStrokeAngle - -RVector TTSourceGenerator::MakeRVector(const TTVectorDesc *ttv, bool pv) { - RVector v; - double len; - - switch (ttv->dir) { - case xRomanDir: - v.x = 1; v.y = 0; - break; - case yRomanDir: - v.x = 0; v.y = 1; - break; - case xItalDir: - case xAdjItalDir: - if (pv) { - v.x = this->slope.y; v.y = -this->slope.x; // not yet distinguishing adjItalDir from italDir - } else { - v.x = 1; v.y = 0; - } - break; - case yItalDir: - case yAdjItalDir: - if (pv) { - v.x = 0; v.y = 1; - } else { - v.x = this->slope.x; v.y = this->slope.y; // not yet distinguishing adjItalDir from italDir - } - break; - case diagDir: - v.x = this->glyph->x[ttv->to] - this->glyph->x[ttv->from]; - v.y = this->glyph->y[ttv->to] - this->glyph->y[ttv->from]; - len = LengthR(v); - v.x /= len; - v.y /= len; - break; - case perpDiagDir: - v.x = this->glyph->y[ttv->to] - this->glyph->y[ttv->from]; - v.y = this->glyph->x[ttv->from] - this->glyph->x[ttv->to]; - len = LengthR(v); - v.x /= len; - v.y /= len; - break; - } - return v; -} // TTSourceGenerator::MakeRVector - -bool TTSourceGenerator::ItalicAngleAllowed(ProjFreeVector *projFreeVector, wchar_t error[]) { - bool italic = xItalDir <= projFreeVector->pv.dir && projFreeVector->pv.dir <= yAdjItalDir; - - if (italic && !this->italic) { - swprintf(error,L"cannot use / (italic angle) or // (adjusted italic angle) unless GLYPHSTROKEANGLE specifies an italic glyph"); - return false; - } - return true; -} // TTSourceGenerator::ItalicAngleAllowed - -bool TTSourceGenerator::AlmostPerpendicular(const TTVectorDesc *pv, const TTVectorDesc *fv, wchar_t error[]) { - RVector rpv,rfv; - - rpv = this->MakeRVector(pv,true); - rfv = this->MakeRVector(fv,false); - - if (Abs(ScalProdRV(rpv,rfv)) >= 1.0/16.0) return false; - - swprintf(error,L"cannot accept vector override (projection and freedom vectors are [almost] perpendicular)"); - return true; -} // TTSourceGenerator::AlmostPerpendicular - -void TTSourceGenerator::SetRounding(bool y, Rounding round, short params, short param[]) { - short i; - - for (i = 0; i < params; i++) this->attrib[param[i]].round[y] = round; -} /* TTSourceGenerator::SetRounding */ - -void TTSourceGenerator::SetItalicStroke(bool phase, wchar_t error[]) { - if (phase) { - if (this->setItalicStrokePhase) - swprintf(error,L"cannot use SETITALICSTROKEPHASE more than once per glyph"); - else - this->setItalicStrokePhase = true; - } else { - if (this->setItalicStrokeAngle) - swprintf(error,L"cannot use SETITALICSTROKEANGLE more than once per glyph"); - else - this->setItalicStrokeAngle = true; - } -} /* TTSourceGenerator::SetItalicStroke */ - -void TTSourceGenerator::Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[]) { - Rounding knotR; - bool negativeDist; - - knotR = this->attrib[knot].round[y]; - if (knot < this->knots - PHANTOMPOINTS || (!y && (knotR == rdtg || knotR == rutg))) { - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error)) return; - if (this->tt) { - this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); - round = round && knotR != roff; // there is a rounding method to be asserted... - // same roundabout way as in Link to maintain philosophy that knot gets rounded, not (absolute or relative) distance - negativeDist = y ? this->V[knot].y < 0 : this->V[knot].x < 0; - if (negativeDist && rdtg <= knotR && knotR <= rutg) knotR = (Rounding)(((short)knotR - (short)rdtg + 1)%2 + (short)rdtg); // rdtg <=> rutg - if (round) this->tt->AssertRounding(knotR); - if (y) this->attrib[knot].cvt = cvt; // make it available to IStroke... - if (cvt < 0) this->tt->MDAP(round,knot); else this->tt->MIAP(round,knot,cvt); // if (still) no cvt... - this->Touched(knot,projFreeVector->fv[0].dir); - } else { - /* nix */ - } - } else if (y) { - swprintf(error,L"cannot YANCHOR the side-bearing points"); - } else { - swprintf(error,L"can XANCHOR the side-bearing points only to grid, down to grid, or up to grid"); - } -} /* TTSourceGenerator::Anchor */ - -void TTSourceGenerator::GrabHereInX(short left, short right, wchar_t error[]) { - short leftDistance,leftCvt,rightDistance,rightCvt; - double dist; - Vector link; - - if (left < this->knots - PHANTOMPOINTS && right < this->knots - PHANTOMPOINTS) { - link = SubV(this->V[left],this->V[this->knots - 2]); - dist = link.x*this->slope.y - link.y*this->slope.x; // signed projection onto the normal to the direction of the main stroke angle - leftDistance = (short)Round(dist); - link = SubV(this->V[this->knots - 1],this->V[right]); - dist = link.x*this->slope.y - link.y*this->slope.x; // signed projection onto the normal to the direction of the main stroke angle - rightDistance = (short)Round(dist); - if (this->tt) { - leftCvt = this->TheCvt(-1,-1,linkGrey,linkX,cvtLsb,leftDistance); - rightCvt = this->TheCvt(-1,-1,linkWhite,linkX,cvtRsb,rightDistance); - if (leftCvt < 0 ) - swprintf(error,L"cannot accept GRABHEREINX (no cvt found from %hi to %hi)",this->knots - 2,left); - else if (rightCvt < 0) - swprintf(error,L"cannot accept GRABHEREINX (no cvt found from %hi to %hi)",right,this->knots - 1); - else { - this->leftAnchor = left; this->rightAnchor = right; - this->tt->CALL24(leftCvt,rightCvt); - } - } - } else - swprintf(error,L"cannot accept GRABHEREINX (%hi is a side-bearing point)",left >= this->knots - PHANTOMPOINTS ? left : right); -} /* TTSourceGenerator::GrabHereInX */ - -short TTSourceGenerator::ProjectedDistance(bool signedDistance, short parent, short child, ProjFreeVector *projFreeVector) { - int32_t distance; - Vector link; - RVector pv; - double temp; - - if (projFreeVector->pv.dir == xRomanDir) { - distance = this->V[child].x - this->V[parent].x; - } else if (projFreeVector->pv.dir == yRomanDir || projFreeVector->pv.dir == yItalDir || projFreeVector->pv.dir == yAdjItalDir) { - // yItalDir and yAdjItalDir have their pv in Y (but their fv in italic direction) - distance = this->V[child].y - this->V[parent].y; - } else { - // general case: need to actually project - if (projFreeVector->pv.dir == xItalDir || projFreeVector->pv.dir == xAdjItalDir) { - // xItalDir and xAdjItalDir have their pv perp italic direction (but their fv in X) - pv.x = this->slope.y; - pv.y = -this->slope.x; - } else { - pv = RDirectionV(this->V[projFreeVector->pv.from],this->V[projFreeVector->pv.to]); - if (projFreeVector->pv.dir == perpDiagDir) { temp = pv.x; pv.x = pv.y; pv.y = -temp; } - } - link = SubV(this->V[child],this->V[parent]); - distance = Round(link.x*pv.x + link.y*pv.y); - } - return (short)(signedDistance ? distance : Abs(distance)); -} // TTSourceGenerator::ProjectedDistance - -void TTSourceGenerator::AssertPositiveProjectedDistance(short *parent, short *child, ProjFreeVector *projFreeVector) { - short temp; - - if (this->ProjectedDistance(true,*parent,*child,projFreeVector) < 0) { - temp = *parent; *parent = *child; *child = temp; - } -} // TTSourceGenerator::AssertPositiveProjectedDistance - -void TTSourceGenerator::Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[]) { - TTVDirection dir; - Vector link; - RVector linkDirection; - double vectProd,scalProd; - short distance = 0,parentC,childC; - LinkColor color; - Rounding deltaR = rthg,parentR,childR; - bool italicLink,lsbLink,rsbLink,negativeDist,negativeMirp; - - linkDirection.x = 0; - linkDirection.y = 0; - - lsbLink = rsbLink = false; - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error)) return; - dir = projFreeVector->pv.dir; - italicLink = this->italic && (dir == xItalDir || dir == xAdjItalDir); - negativeDist = negativeMirp = false; - if (y) { - color = this->glyph->TheColor(parent,child); - link = SubV(this->V[child],this->V[parent]); linkDirection = RDirectionV(this->V[child],this->V[parent]); - negativeDist = link.y < 0; - if (!this->legacyCompile) - { - distance = this->ProjectedDistance(false, parent, child, projFreeVector); - } - else - { - distance = (short)Abs(link.y); - } - } else { // check for links related to GrabHereInX, using predefined colors and cvt numbers - if ((parent == this->leftAnchor && child == this->knots - 2) || (parent == this->knots - 2 && child == this->leftAnchor)) { - lsbLink = true; negativeMirp = parent == this->leftAnchor; - if (dist) swprintf(error,L"cannot use an XDIST command when a GRABHEREINX command has defined a cvt number"); - if (cvt >= 0) swprintf(error,L"cannot override a cvt number defined via a GRABHEREINX command"); - color = linkGrey; cvt = LSBTMP; - } else if ((parent == this->rightAnchor && child == this->knots - 1) || (parent == this->knots - 1 && child == this->rightAnchor)) { - rsbLink = true; negativeMirp = parent == this->rightAnchor; - if (dist) swprintf(error,L"cannot use an XDIST command when a GRABHEREINX command has defined a cvt number"); - if (cvt >= 0) swprintf(error,L"cannot override a cvt number defined via a GRABHEREINX command"); - color = linkWhite; cvt = RSBTMP; - } else { - color = this->glyph->TheColor(parent,child); - link = SubV(this->V[child],this->V[parent]); linkDirection = RDirectionV(this->V[child],this->V[parent]); - if (italicLink) { - vectProd = link.x*this->slope.y - link.y*this->slope.x; - negativeDist = vectProd < 0; distance = (short)Round(Abs(vectProd)); // unsigned distance... project onto the normal to the direction of the main stroke angle - } else { - negativeDist = link.x < 0; - if (!this->legacyCompile) - { - distance = this->ProjectedDistance(false, parent, child, projFreeVector); - } - else - { - distance = (short)Abs(link.x); - } - } - } - } - parentR = this->attrib[parent].round[y]; - childR = this->attrib[child].round[y]; - - if (this->tt) this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); - - scalProd = linkDirection.x*this->slope.x + linkDirection.y*this->slope.y; - if (parentR == childR && !lsbLink && !rsbLink && (distance == 0 || (italicLink && scalProd > this->cosF1))) { // angle between link and slope < 0.5° - if (this->tt) { - this->tt->AssertRefPoint(0,parent); - // ALIGNRP is less optimal than (but equivalent to) an unrounded MDRP, since for chains of Dists, - // we keep setting the reference point explicitely, while MDRP can simply move it along - if (distance == 0) { - this->tt->AssertRounding(rtg); - this->tt->MDRP(false,true,linkGrey,child); // almost same as in ::Align - } else - this->tt->ALIGNRP(child); - } - } else { - if (this->tt) { - if (!this->legacyCompile) - { - // we simply round the distance according to the child point's rounding method; see also further comments below - deltaR = postRoundFlag ? roff : childR; - // if (parentR == rthg && childR == rthg) deltaR = rtg; - // else if (parentR == rthg && childR == rtg) deltaR = rthg; - } - else - { - deltaR = postRoundFlag ? roff : deltaRounding[parentR][childR]; - } - if (negativeDist && rdtg <= deltaR && deltaR <= rutg) deltaR = (Rounding)(((short)deltaR - (short)rdtg + 1)%2 + (short)rdtg); // rdtg <=> rutg - if (minDists < 0) { // no minDist override => get viable defaults - minDists = 0; - if (color == linkBlack) { - for (parentC = 0; (parentC < this->glyph->numContoursInGlyph && this->glyph->endPoint[parentC] < parent); parentC++); - for (childC = 0; (childC < this->glyph->numContoursInGlyph && this->glyph->endPoint[childC] < child); childC++); - if ((distance >= this->emHeight/100 && parentC == childC) || (distance >= this->emHeight/50 && parentC != childC)) { - // this uses just the same assumptions as did the old compiler to guess whether or not it makes sense to assert a minimum distance - jumpPpemSize[0] = 1; - if (!this->legacyCompile) - { - pixelSize[0] = defaultMinDistAmount[childR][childR]; - } - else - { - pixelSize[0] = defaultMinDistAmount[parentR][childR]; - } - if (negativeDist && rdtg <= deltaR && deltaR <= rutg) pixelSize[0] = (pixelSize[0] + one6)%two6; // one6 <=> 0 - if (pixelSize[0] > 0) minDists++; - } - } - } - this->tt->AssertMinDist(minDists,jumpPpemSize,pixelSize); - if (!postRoundFlag && deltaR != roff) this->tt->AssertRounding(deltaR); - this->tt->AssertRefPoint(0,parent); - } - if (italicLink && !lsbLink && !rsbLink && scalProd > this->cosF2) { // angle between link and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION - if (this->tt) this->tt->MDRP(minDists > 0,deltaR != roff,color,child); - } else if (this->tt) { - if (!dist && cvt < 0) { // Link command and (still) no cvt => get default from cvt table - cvt = this->TheCvt(parent,child,color,y ? linkY : linkX,category,distance); - } - if (dist || cvt < 0) { // Dist command or Link command and (still) no cvt (depends on how cvt "comments" were setup) - this->tt->MDRP(minDists > 0,deltaR != roff,color,child); - } else { - this->tt->AssertAutoFlip(!lsbLink && !rsbLink); // autoflip affects only MIRP - this->tt->MIRP(minDists > 0,deltaR != roff,color,child,cvt,negativeMirp); - } - } // else cvt entered in GrabHereInX - if (this->tt && !postRoundFlag && deltaR == roff && childR != roff) { // can't do otherwise - // I think the original idea here was to ensure that the rounding method is an attribute of the knot, - // i.e. if I wanted the knot "down-to-grid" then it will end up "down-to-grid", and not the distance - // inbetween. As long as our fv and pv are X or Y, this is achieved by either adjusting the rounding - // method of the distance accordingly (deltaRounding table), or else don't round the distance and do - // round the knot afterwards. As soon as we're using a pv other than X or Y, and especially when we're - // using the dpv, the whole idea makes little sense, since the respective knots are no longer parallel - // to any grid. Furthermore, when using the dpv, rounding afterwards would be pretty useless anyway, - // because MDAP never uses the dpv in the first place. Therefore, and currently #ifdef'd for VTT_PRO_SP_YAA_AUTO_COM - // only, the distance is rounded as per the rounding method of the child point. - this->tt->AssertRounding(childR); - this->tt->MDAP(true,child); - } - } - if (this->tt && postRoundFlag && childR != roff) { - this->tt->AssertFreeProjVector(xRomanDir); // only allowed on XDist, XLink - this->tt->AssertRounding(childR); - this->tt->MDAP(true,child); - } - if (this->tt) this->Touched(child,projFreeVector->fv[0].dir); - *actualCvt = lsbLink || rsbLink ? invalidCvtNum : cvt; -} /* TTSourceGenerator::Link */ - -/***** have to test this on the visual level, because we might end up with the following scenario: -- yinterpolate two children with exactly the same y-coordinate (e.g. points 1 and 47 in Times New Roman 'M'). -- round first of them to grid, translating to a YIPAnchor and a YInterpolate -- YInterpolate has rounded interpolee as parent of unrounded interpolee (Ian's and Vinnie's wish) -- but now unrounded interpolee is at same y-coordinate as its (temporary) parent -bool ValidateInterpolee(bool y, short parent0, short child, short parent1, Vector V[], wchar_t error[]); -bool ValidateInterpolee(bool y, short parent0, short child, short parent1, Vector V[], wchar_t error[]) { - int32_t low,mid,high; - short parent; - wchar_t dir; - - if (y) { - low = Min(V[parent0].y,V[parent1].y); - high = Max(V[parent0].y,V[parent1].y); - mid = V[child].y; - } else { - low = Min(V[parent0].x,V[parent1].x); - high = Max(V[parent0].x,V[parent1].x); - mid = V[child].x; - } - if (low < mid && mid < high) return true; // accept - dir = 'x' + (y & true); - if (low == mid || mid == high) { - parent = y ? (mid == V[parent0].y ? parent0 : parent1) : (mid == V[parent0].x ? parent0 : parent1); - swprintf(error,L"cannot %cInterpolate child point %hi (cannot be on same %c-coordinate as its parent point %hi)",Cap(dir),child,dir,parent); - } else { - swprintf(error,L"cannot %cInterpolate child point %hi (cannot be outside the range of %c-coordinates defined by its parent points %hi and %hi)",Cap(dir),child,dir,parent0,parent1); - } - return false; -} // ValidateInterpolee -*****/ - -void TTSourceGenerator::Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[]) { - short i,r; - Rounding rounding[maxParams]; - - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - if (this->tt) { - this->tt->AssertRefPointPair(1,2,parent0,parent1); - - if (SameVectorsForAllChildren(projFreeVector,children)) { - if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error)) return; - this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); - if (children <= 2) { // optimise for 2 or less interpolations - for (i = 0; i < children; i++) this->tt->IP(1,&child[i]); - } else { - this->tt->SLOOP(children); - this->tt->IP(children,child); - } - for (i = 0; i < children; i++) this->Touched(child[i],projFreeVector->fv[i].dir); - } else { - for (i = 0; i < children; i++) { - if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error)) return; - this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[i]);; - this->tt->IP(1,&child[i]); - this->Touched(child[i],projFreeVector->fv[i].dir); - } - } - if (round || postRoundFlag) { - if (postRoundFlag) this->tt->AssertFreeProjVector(xRomanDir); // only allowed on XInterpolate, XIPAnchor - for (i = 0; i < children; i++) rounding[i] = this->attrib[child[i]].round[y]; - for (r = (short)rthg; r <= (short)rutg; r++) this->CondRoundInterpolees(y,children,child,rounding,(Rounding)r); - } - // else, strictly speaking, we should set this->attrib[child[i]].round[y] to roff (the default is rtg, which gets modified by Interpolate to roff) - } -} /* TTSourceGenerator::Interpolate */ - -void TTSourceGenerator::BeginCodePath(short fpgmBias, wchar_t error[]) { - this->tt->SetFunctionNumberBias(fpgmBias); - this->InitCodePathState(); -} // TTSourceGenerator::BeginCodePath - -void TTSourceGenerator::EndCodePath(wchar_t error[]) { - this->TermCodePathState(); -} // TTSourceGenerator::EndCodePath - -// Notice that for the implementation of the new Rendering Environment Specific VTT Talk commands we tend to set the reference points from within the respective functions hence there is no need to assert them here. -// The main reason for doing so is the absence of "get-reference-point" instructions: the functions implementing these commands tend to need the parents' (refernece points') original and/or current coordinates. -void TTSourceGenerator::ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[]) { - if (this->tt == NULL) return; - if (child >= this->knots - PHANTOMPOINTS) { swprintf(error,L"cannot Res%cAnchor the side-bearing points",y ? L'Y' : L'X'); return; } - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error)) return; - this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); - this->tt->ResMIAP(child,cvt); - this->Touched(child,projFreeVector->fv[0].dir); -} // TTSourceGenerator::ResAnchor - -void TTSourceGenerator::ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[]) { - if (this->tt == NULL) return; - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error)) return; - // limited pv/fv implemented so far only, asserted within fn - this->tt->ResIPMDAP(projFreeVector->pv.dir,postRoundFlag,parent0,child,parent1); - this->Touched(child,projFreeVector->fv[0].dir); -} // TTSourceGenerator::ResIPAnchor - -void TTSourceGenerator::ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[]) { - bool useMinDist; - - if (this->tt == NULL) return; -// if (parent >= this->knots - PHANTOMPOINTS && child >= this->knots - PHANTOMPOINTS) { swprintf(error,L"cannot Res%cLink the advance width",y ? 'Y' : 'X'); return; } - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error)) return; - - useMinDist = minDists > 0 || (minDists < 0 && this->glyph->TheColor(parent,child) == linkBlack); - - this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); - this->tt->ResMIRP(parent,child,cvt,useMinDist); - this->Touched(child,projFreeVector->fv[0].dir); -} // TTSourceGenerator::ResLink - -void TTSourceGenerator::ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[]) { - if (this->tt == NULL) return; - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error)) return; - // ResIPMIRP doesn't like negative distances and complementary phases - this->AssertPositiveProjectedDistance(&parent,&child,projFreeVector); - this->AssertPositiveProjectedDistance(&grandParent0,&grandParent1,projFreeVector); - // limited pv/fv implemented so far only, asserted within fn - this->tt->ResIPMIRP(projFreeVector->pv.dir,strokeOptimizationFlag,grandParent0,parent,child,cvt,grandParent1); - this->Touched(parent,projFreeVector->fv[0].dir); - this->Touched(child, projFreeVector->fv[1].dir); -} // TTSourceGenerator::ResIPLink - -void TTSourceGenerator::ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[]) { - TTVectorDesc pv[2]; - - if (this->tt == NULL) return; - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - pv[0].dir = perpDiagDir; pv[0].from = parent0; pv[0].to = child1; // criss-crossed... - pv[1].dir = perpDiagDir; pv[1].from = parent1; pv[1].to = child0; // ...diagonal links - for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&pv[i],&projFreeVector->fv[i],error)) return; - // pv will be dealt with in fn (that's the very raison d'être of DLink...) - this->tt->ResDDMIRP(parent0,child0,projFreeVector->fv[0],cvt0,parent1,child1,projFreeVector->fv[1],cvt1); - this->Touched(child0,projFreeVector->fv[0].dir); - this->Touched(child1,projFreeVector->fv[1].dir); -} // TTSourceGenerator::ResDDLink - -// it looks like the following three methods are quite similar, but they have rather different semantics: -// ResIPDLink constrains a pair of strokes in x or y by a single constraint each, while ResIPDDLink/ResIIPDLink constrain a diagonal/an italic stroke by a pair of constraints -void TTSourceGenerator::ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]) { - TTVDirection dir = y ? yRomanDir : xRomanDir; - - // strokeOptimizationFlag not yet supported - if (this->tt == NULL) return; -// if (!this->ItalicAngleAllowed(projFreeVector,error)) return; -// for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error)) return; - // ResIPDMIRP doesn't like negative distances and complementary phases - this->AssertPositiveProjectedDistance(&parent0,&child0,projFreeVector); - this->AssertPositiveProjectedDistance(&parent1,&child1,projFreeVector); - this->AssertPositiveProjectedDistance(&grandParent0,&grandParent1,projFreeVector); - // pv/fv limited to x or y by syntax so far - this->tt->ResIPDMIRP(projFreeVector->pv.dir,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1); - this->Touched(parent0,dir); - this->Touched(child0, dir); - this->Touched(child1, dir); - this->Touched(parent1,dir); -} // TTSourceGenerator::ResIPDLink - -void TTSourceGenerator::ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]) { - TTVDirection dir = y ? yRomanDir : xRomanDir; - - // strokeOptimizationFlag not yet supported - if (this->tt == NULL) return; -// if (!this->ItalicAngleAllowed(projFreeVector,error)) return; -// for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error)) return; - /***** - // ResIPDDMIRP doesn't like negative distances and complementary phases - this->AssertPositiveProjectedDistance(&parent0,&child0,projFreeVector); - this->AssertPositiveProjectedDistance(&parent1,&child1,projFreeVector); - *****/ - this->AssertPositiveProjectedDistance(&grandParent0,&grandParent1,projFreeVector); - this->tt->ResIPDDMIRP(projFreeVector->pv.dir,grandParent0,parent0,child0,projFreeVector->fv[0],cvt0,parent1,child1,projFreeVector->fv[1],cvt1,grandParent1); - this->Touched(parent0,dir); - this->Touched(child0, projFreeVector->fv[0].dir); - this->Touched(child1, projFreeVector->fv[1].dir); - this->Touched(parent1,dir); -} // TTSourceGenerator::ResIPDDLink - -void TTSourceGenerator::ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]) { - if (this->tt == NULL) return; - if (!this->ItalicAngleAllowed(projFreeVector,error)) return; - for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error)) return; - // pv xItalDir, asserted within fn, hence fv = xRomanDir - this->tt->ResIIPDMIRP(grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1); - this->Touched(parent0,xRomanDir); - this->Touched(child0, xRomanDir); - this->Touched(child1, xRomanDir); - this->Touched(parent1,xRomanDir); -} // TTSourceGenerator::ResIIPDLink - -void TTSourceGenerator::Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[]) { - if (this->tt) { - if (ppem0 == noPpemLimit && ppem1 == noPpemLimit) { - this->tt->ISECT(intersection,line0start,line0end,line1start,line1end); - } else if (ppem1 == noPpemLimit) { - this->tt->IfPpemBelow(ppem0); - this->tt->ISECT(intersection,line0start,line0end,line1start,line1end); - this->tt->Else(); - this->Align(fvStandard,line0start,1,&intersection,line0end,ppem0,error); - this->Align(fvStandard,line1start,1,&intersection,line1end,noPpemLimit,error); - this->tt->End(true); - } else { // both directions have ppem limits - this->tt->IfPpemBelow(Min(ppem0,ppem1)); - this->tt->ISECT(intersection,line0start,line0end,line1start,line1end); - this->tt->Else(); - this->Align(fvStandard,line0start,1,&intersection,line0end,ppem0,error); - this->Align(fvStandard,line1start,1,&intersection,line1end,ppem1,error); - this->tt->End(true); - } - this->Touched(intersection,diagDir); - } -} // TTSourceGenerator::Intersect - -bool ClassifyAlign(Vector parent0, Vector child, Vector parent1, short ppem) { // within rectangular hull of ±0.5° from "parent line"? - double tanAlignTolerance; - Vector p,q; - int32_t pq,p2; - - if (ppem > 0) return true; // ppem limit specified, hence we'll gladly align anything - - tanAlignTolerance = tan(Rad(ALIGN_TOLERANCE)); - p = SubV(parent1,parent0); - q = SubV(child,parent0); - p2 = p.x*p.x + p.y*p.y; - pq = ScalProdV(p,q); - return 0 <= pq && pq <= p2 && Abs(VectProdV(p,q)) <= p2*tanAlignTolerance; -} // ClassifyAlign - -void TTSourceGenerator::Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[]) { - short i,ch,iChildren[2],iChild[2][maxParams],refPoint[maxParams][2]; - int32_t minX,minY,x,y,maxX,maxY; - AlignParam *align; - wchar_t buf[8*maxParams]; - RVector alignDirection = RDirectionV(this->V[parent0],this->V[parent1]); - FVMTDirection fvmt[maxParams]; - - if (this->tt) { - switch (fvOverride) { - case fvOldMethod: // Align - swprintf(buf,L"/* Align [%hi...%hi] */",parent0,parent1); this->tt->Emit(buf); - - this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); - for (i = 0; i < children; i++) { - ch = child[i]; - this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); - if (ClassifyAlign(this->V[parent0],this->V[ch],this->V[parent1],ppem)) - this->tt->ALIGNRP(ch); - else { - this->tt->AssertRounding(rdtg); - this->tt->MDRP(false,true,linkGrey,ch); - } - this->Touched(ch,this->tt->FVDir()); - } - break; - case fvForceX: // XAlign - case fvForceY: // YAlign - case fvStandard: // DAlign - swprintf(buf,L"/* %cAlign [%hi...%hi] */",fvOverride == fvStandard ? L'D' : (fvOverride == fvForceX ? L'X' : L'Y'),parent0,parent1); this->tt->Emit(buf); - - minX = Min(this->V[parent0].x,this->V[parent1].x); maxX = Max(this->V[parent0].x,this->V[parent1].x); - minY = Min(this->V[parent0].y,this->V[parent1].y); maxY = Max(this->V[parent0].y,this->V[parent1].y); - - for (i = iChildren[0] = iChildren[1] = 0; i < children; i++) { - ch = child[i]; - - fvmt[i] = CalcAlignFVMT(fvOverride,parent0,parent1,ch,alignDirection,&refPoint[i][0],&refPoint[i][1]); - // if (fvOverride != fvStandard) { - // always touch point in both directions or else aligning is undermined - x = this->V[ch].x; - y = this->V[ch].y; - if (fvmt[i] == fvOnX && !this->attrib[ch].touched[yRomanDir] && !this->ySmooth && minY < y && y < maxY) iChild[true][iChildren[true]++] = ch; - if (fvmt[i] == fvOnY && !this->attrib[ch].touched[xRomanDir] && !this->xSmooth && minX < x && x < maxX) iChild[false][iChildren[false]++] = ch; - // } - - } - if (iChildren[true] > 0) this->Interpolate(true,&this->yRomanPV,false,parent0,iChildren[true],iChild[true],parent1,false,error); - if (iChildren[false] > 0) this->Interpolate(false,&this->xRomanPV,false,parent0,iChildren[false],iChild[false],parent1,false,error); - - this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); - // /****** if (ppem >= 0) *****/ this->tt->AssertRoundingBelowPpem(rdtg,ppem); - if (ppem != 1) this->tt->RoundDownToGridBelowPpem(ppem); // Jelle special optimisation - this->tt->AssertTTVonLine(dpv,parent0,parent1,this->V[parent0],this->V[parent1],true); - for (i = 0; i < children; i++) { - ch = child[i]; - this->AssertFVMT(fvmt[i],refPoint[i][0],refPoint[i][1]); - this->tt->MDRP(false,ppem != 1/***** true *****//***** ppem >= 0 *****/,linkGrey,ch); - this->Touched(ch,this->tt->FVDir()); - } - - break; - - // swprintf(buf,L"/* DAlign [%hi...%hi] */",parent0,parent1); this->tt->Emit(buf); - - // this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); - // if (ppem >= 0) this->tt->AssertRoundingBelowPpem(rdtg,ppem); - // for (i = 0; i < children; i++) { - // ch = child[i]; - // this->AssertVectorsAtPotentialJunction(dpv,parent0,parent1,ch); - // this->tt->MDRP(false,ppem >= 0,linkGrey,ch); - // this->Touched(ch,this->tt->FVDir()); - // } - // break; - default: - break; - } - - // ***** an experimental variant ***** - // alignDirection = RDirectionV(this->V[parent0],this->V[parent1]); - // this->tt->IfPpemBelow(ppem); - // tt->AssertTTVonLine(pv,parent0,parent1,this->V[parent0],this->V[parent1],true); - // for (i = 0; i < children; i++) { - // ch = child[i]; - // this->AssertFVMT(parent0,parent1,ch,alignDirection); - // this->tt->ALIGNRP(ch); - // this->Touched(ch,this->tt->FVDir()); - // } - // this->tt->Else(); - // tt->AssertTTVonLine(dpv,parent0,parent1,this->V[parent0],this->V[parent1],true); - // for (i = 0; i < children; i++) { - // ch = child[i]; - // this->AssertFVMT(parent0,parent1,ch,alignDirection); - // this->tt->MDRP(false,false,linkGrey,ch); - // this->Touched(ch,this->tt->FVDir()); - // } - // this->tt->End(); - // ***** another experimental variant - // this->tt->IfPpemBelow(ppem); - // for (i = 0; i < children; i++) { - // ch = child[i]; - // this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); - // this->tt->ALIGNRP(ch); - // this->Touched(ch,this->tt->FVDir()); - // } - // this->tt->Else(); - // for (i = 0; i < children; i++) { - // ch = child[i]; - // this->AssertVectorsAtPotentialJunction(dpv,parent0,parent1,ch); - // this->tt->MDRP(false,false,linkGrey,ch); - // this->Touched(ch,this->tt->FVDir()); - // } - // this->tt->End(); - // ***** - - align = new AlignParam; - align->parent0 = parent0; align->parent1 = parent1; - align->children = children; - for (i = 0; i < children; i++) align->child[i] = child[i]; - this->aligns->InsertAtEnd(align); - } - -// if (this->tt) { -// this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); -// if (ppem < 0) { -// for (i = 0; i < children; i++) { -// ch = child[i]; -// this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); -// if (ClassifyAlign(this->V[parent0],this->V[ch],this->V[parent1],ppem)) -// this->tt->ALIGNRP(ch); -// else -// this->tt->MDRP(false,false,linkGrey,ch); -// this->Touched(ch,this->tt->FVDir()); -// } -// } else { -// this->tt->AssertRoundingBelowPpem(rdtg,ppem); -// for (i = 0; i < children; i++) { -// ch = child[i]; -// this->AssertVectorsAtPotentialJunction(dpv,parent0,parent1,ch); -// this->tt->MDRP(false,true,linkGrey,ch); -// this->Touched(ch,this->tt->FVDir()); -// } -// } -// } -} // TTSourceGenerator::Align - -/***** -void TTSourceGenerator::Align(short parent0, short children, short child[], short parent1, short ppem, wchar_t error[]) { - short i,ch; - RVector parentDir,childDir; - - if (this->tt) { - parentDir = RDirectionV(this->V[parent1],this->V[parent0]); - for (i = 0; i < children; i++) { - ch = child[i]; - this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); - this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); - childDir = RDirectionV(this->V[ch],this->V[parent0]); - // distance = childV.x*slope.y - childV.y*slope.x; // Sampo's test: project onto normal to "parent line" - // if (Abs(distance) < this->emHeight/2048.0 + 0.707107) // close enough to be considered "aligned"... - if (childDir.x*parentDir.x + childDir.y*parentDir.y > this->cosF1) - this->tt->ALIGNRP(ch); - else { - this->tt->AssertRounding(rdtg); - this->tt->MDRP(false,true,linkGrey,ch); - } - this->Touched(ch,this->tt->FVDir()); - } - } -} // TTSourceGenerator::Align -*****/ - -void TTSourceGenerator::Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[]) { - TTVDirection dir; - short i; - - if (this->tt) { - dir = y ? yRomanDir : xRomanDir; // no [adjusted] italic directions... - this->tt->AssertFreeProjVector(dir); - if (knots > 1) this->tt->SLOOP(knots); - this->tt->SHPIX(knots,knot,amount); - for (i = 0; i < knots; i++) this->Touched(knot[i],dir); - } -} /* TTSourceGenerator::Move */ - -void TTSourceGenerator::Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[]) { - short i,rp; - - if (this->tt) { - rp = this->tt->AssertEitherRefPointOnKnot(1,2,parent); - if (SameVectorsForAllChildren(projFreeVector,children)) { - this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); - if (children <= 2) { // optimise for 2 or less shifted children - for (i = 0; i < children; i++) this->tt->SHP(rp,1,&child[i]); - } else { - this->tt->SLOOP(children); - this->tt->SHP(rp,children,child); - } - for (i = 0; i < children; i++) this->Touched(child[i],projFreeVector->fv[i].dir); - } else { - for (i = 0; i < children; i++) { - this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[i]);; - this->tt->SHP(rp,1,&child[i]); - this->Touched(child[i],projFreeVector->fv[i].dir); - } - } - } -} /* TTSourceGenerator::Shift */ - -short RectilinearDistanceOfDiagonal(bool x, const Vector V0, const Vector V1, const RVector strokeDirection) { - Vector link; - double dist; - - link = SubV(V1,V0); - dist = x ? link.x - link.y*strokeDirection.x/strokeDirection.y - : link.y - link.x*strokeDirection.y/strokeDirection.x; - return Abs((short)Round(dist)); -} // RectilinearDistanceOfDiagonal - -bool ClassifyStroke(Vector A1, Vector A2, Vector B1, Vector B2, short ppem, bool *crissCross, RVector *strokeDirection, bool *xLinks, short distance[], wchar_t error[]) { - double cosF0; - int32_t sgn0,sgn1; - Vector aux; - RVector leftDirection,rightDirection; - - sgn0 = VectProdP(A1,B1,A1,A2); sgn0 = Sgn(sgn0); - sgn1 = VectProdP(A1,B1,A1,B2); sgn1 = Sgn(sgn1); - *crissCross = sgn0 != sgn1; - if (*crissCross) { aux = B1; B1 = B2; B2 = aux; } // we're criss-crossing; swap the second pair for further analysis - - leftDirection = RDirectionV(B1,A1); - rightDirection = RDirectionV(B2,A2); - cosF0 = cos(Rad(MAXSTROKESIDEANGLEDIFFERENCE)); - if (leftDirection.x*rightDirection.x + leftDirection.y*rightDirection.y < (ppem < 0 ? cosF0 : 2*cosF0*cosF0 - 1)) { // edges are "anti-parallel" || not nearly-parallel, RScalProdV(left,right);... - swprintf(error,L"cannot accept (X|Y)STROKE (edges differ by %f degrees or more)",(double)(ppem < 0 ? MAXSTROKESIDEANGLEDIFFERENCE : 2*MAXSTROKESIDEANGLEDIFFERENCE)); - return false; - } - *strokeDirection = RAvgDirectionV(leftDirection,rightDirection); - *xLinks = Abs(strokeDirection->x) <= Abs(strokeDirection->y); - distance[0] = RectilinearDistanceOfDiagonal(*xLinks,A1,A2,*strokeDirection); - distance[1] = RectilinearDistanceOfDiagonal(*xLinks,B1,B2,*strokeDirection); - return true; // by now -} // ClassifyStroke - -void TTSourceGenerator::AssertStrokePhaseAngle(FVOverride fv, bool leftStationary[], short knot[]) { - short A1,B1,lsb; - wchar_t code[64]; - - // for the the italic stroke phase/angle adjustment to make sense, it always looks at the first knot pair, - // regardless of whether or not this will be the mirp side further down. However, in these cases linking - // criss-cross doesn't make sense, for it would undo the effects of an adjusted italic stroke phase. - - A1 = !leftStationary[0]; - B1 = 2+!leftStationary[1]; - if (fv != fvOldMethod && this->V[knot[B1]].y < this->V[knot[A1]].y) A1 = B1; // pull down the lower of the two parents - lsb = this->knots - PHANTOMPOINTS; - if (this->setItalicStrokePhase) { - if (this->V[knot[A1]].y != 0) { - if (fv != fvOldMethod && this->attrib[knot[A1]].touched[yRomanDir]) { // remember current y position - this->tt->AssertFreeProjVector(yRomanDir); - swprintf(code,L"GC[N], %hi",knot[A1]); this->tt->Emit(code); - this->tt->Emit(L"#BEGIN"); // need to start new block or else pre-push interferes with GC - } - this->tt->AssertFreeProjVector(yItalDir); - this->tt->AssertRefPoint(0,lsb); - this->tt->ALIGNRP(knot[A1]); - this->Touched(knot[A1],yItalDir); - } - this->tt->AssertFreeProjVector(xRomanDir); // this used to be a call to fn 23, suggesting to be able to change the italic stroke - this->tt->AssertRounding(rtg); // phase globally. However, fn 23 can't be changed without changing the compiler, for - this->tt->MDAP(true,knot[A1]); // it has side effects which the compiler has to be aware of!!! - this->Touched(knot[A1],xRomanDir); - } - if (this->setItalicStrokeAngle) { - this->tt->AssertFreeProjVector(xAdjItalDir); - this->tt->AssertRefPoint(0,knot[A1]); - this->tt->ALIGNRP(knot[(A1+2)%4]); - this->Touched(knot[(A1+2)%4],xAdjItalDir); - if (this->V[knot[A1]].y != 0) { - this->tt->AssertFreeProjVector(yAdjItalDir); - this->tt->AssertRefPoint(0,lsb); - if (fv == fvOldMethod || !this->attrib[knot[A1]].touched[yRomanDir]) { - this->tt->MDRP(false,false,linkGrey,knot[A1]); - } else { // push it back up where it once belonged - this->tt->Emit(L"#END"); // end block started above - this->tt->AssertPVonCA(yRomanDir); - swprintf(code,L"SCFS[], %hi, *",knot[A1]); this->tt->Emit(code); - } - this->Touched(knot[A1],yAdjItalDir); - } - } -} // TTSourceGenerator::AssertStrokePhaseAngle - -void TTSourceGenerator::Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[]) { - if (fvOverride == fvOldMethod) { - short i,/*lsb,*/mirpSide,A2,A1,B2,B1,cvtIndex[2],distance[2]; - RVector strokeDirection; - bool crissCross,xLinks,iStroke; - wchar_t buf[64]; - - if (ClassifyStroke(this->V[knot[0]],this->V[knot[1]],this->V[knot[2]],this->V[knot[3]],ppem,&crissCross,&strokeDirection,&xLinks,distance,error)) { - iStroke = strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y > this->cosF2; // angle between stroke and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION - for (i = 0; i < 4; i++) { - this->attrib[knot[i]].dStroke = true; - this->attrib[knot[i]].iStroke |= iStroke; - } - if (this->tt) { - for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,linkDiag,cvtStroke,Abs(distance[i])) : cvt; - if (cvtIndex[0] < 0 && cvtIndex[1] < 0) - swprintf(error,L"cannot accept STROKE (no cvt number found)"); - else { - if (cvtIndex[0] < 0) cvtIndex[0] = cvtIndex[1]; - else if (cvtIndex[1] < 0) cvtIndex[1] = cvtIndex[0]; - mirpSide = Abs(distance[1]) > Abs(distance[0]) ? 1 : 0; // mirp the larger end - cvt = cvtIndex[mirpSide]; - - swprintf(buf,L"/* Stroke [%hi,%hi]%hi-[%hi,%hi]%hi */",knot[0],knot[1],distance[0],knot[2],knot[3],distance[1]); this->tt->Emit(buf); - - if (iStroke) this->AssertStrokePhaseAngle(fvOverride,leftStationary,knot); - - /***** - // for the the italic stroke phase/angle adjustment to make sense, it always looks at the first knot pair, - // regardless of whether or not this will be the mirp side further down. However, in these cases linking - // criss-cross doesn't make sense, for it would undo the effects of an adjusted italic stroke phase. - - A1 = !leftStationary[0]; - lsb = this->knots - PHANTOMPOINTS; - if (this->italicStrokePhase && iStroke) { - if (this->V[knot[A1]].y != 0) { - this->tt->AssertFreeProjVector(yItalDir); - this->tt->AssertRefPoint(0,lsb); - this->tt->ALIGNRP(knot[A1]); - this->Touched(knot[A1],yItalDir); - } - this->tt->AssertFreeProjVector(xRomanDir); // this used to be a call to fn 23, suggesting to be able to change the italic stroke - this->tt->AssertRounding(rtg); // phase globally. However, fn 23 can't be changed without changing the compiler, for - this->tt->MDAP(true,knot[A1]); // it has side effects which the compiler has to be aware of!!! - this->Touched(knot[A1],xRomanDir); - } - if (this->italicStrokeAngle && iStroke) { - this->tt->AssertFreeProjVector(xAdjItalDir); - this->tt->AssertRefPoint(0,knot[A1]); - this->tt->ALIGNRP(knot[A1+2]); - this->Touched(knot[A1+2],xAdjItalDir); - if (this->V[knot[A1]].y != 0) { - this->tt->AssertFreeProjVector(yAdjItalDir); - this->tt->AssertRefPoint(0,lsb); - this->tt->MDRP(false,false,linkGrey,knot[A1]); - this->Touched(knot[A1],yAdjItalDir); - } - } - *****/ - - // leftStationary[0] := [A1]--->[A2] - // leftStationary[1] := [B1]--->[B2] - // [B1] mirpSide := either 0 for [A1]<--->[A2] or 1 for [B1]<--->[B2] depending on which has the larger cvt entry (if any at all...) - // * *** - // * [B2] to understand all combinations, it is enough to assume STROKE([A1]--->[A2],[B1]--->[B2]) with the mirpSide = [A1]<--->[A2] - // * * the other combinations are taken care of by 4 assignments below. Then, what the Stroke does, is essentially: - // * * - // * * SDPTL[R] [A1],[(A1+2)%4] - // * * SFVTPV[] // either (A2 not assumed to be at a junction) - // * * SFVTL[r] [A2],neighbour // or (A2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction - // * * MDAP[r] [A1] - // * * DMIRP [A2] - // * * SDPTL[R] [(B1+2)%4],[B1] - // * * SFVTPV[] // again, either (B2 not assumed to be at a junction) - // * * SFVTL[r] [B2],neighbour // or (B2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction - // * *[A2] MDRP[mAssertVectorsAtPotentialJunction(dpv,knot[A1],knot[(A1+2)%4],knot[A2]); - this->tt->MDAP(false,knot[A1]); // don't round stationary point - this->Touched(knot[A1],this->tt->FVDir()); - this->tt->AssertRounding(rtg); - this->tt->AssertRefPoint(0,knot[A1]); - this->tt->AssertAutoFlip(true); - this->tt->DMIRP(knot[A2],cvt,knot[A1],knot[(A1+2)%4]); - this->Touched(knot[A2],this->tt->FVDir()); - - this->AssertVectorsAtPotentialJunction(dpv,knot[(B1+2)%4],knot[B1],knot[B2]); - // this->tt->AssertRoundingBelowPpem(rdtg,ppem); - this->tt->RoundDownToGridBelowPpem(ppem); - this->tt->AssertRefPoint(0,knot[(B2+2)%4]); - this->tt->MDRP(false,true,linkGrey,knot[B2]); - this->Touched(knot[B2],this->tt->FVDir()); - } - } - } - } else { // one of the new methods - short i,A2,A1,B2,B1,distance[2],refPoint0,refPoint1; - RVector strokeDirection; - double dotProd; - bool crissCross,xLinks,iStroke; - DiagParam *diagonal; - wchar_t buf[64]; - FVMTDirection fvmt; - - if (ClassifyStroke(this->V[knot[0]],this->V[knot[1]],this->V[knot[2]],this->V[knot[3]],ppem,&crissCross,&strokeDirection,&xLinks,distance,error)) { - if (cvt < 0) - cvt = this->TheCvt(-1,-1,linkBlack,linkDiag,cvtStroke,Abs(distance[0])); - if (cvt < 0) - swprintf(error,L"cannot accept DIAGONAL (no cvt number found)"); - else { - - // [B1] leftStationary[0] := [A1]--->[A2] - // * *** leftStationary[1] := [B1]--->[B2] - // * [B2] to understand all combinations, it is enough to assume DIAGONAL([A1]--->[A2],[B1]--->[B2]) - // * * the other combinations are taken care of by 4 assignments below. Then, what the DIAGONAL does, is essentially: - // * * - // * * SDPTL[R] [A1],[(A1+2)%4] - // * * SFV??[] // set freedom vector according to the 15 rules in ::AssertFVMT - // * * MDAP[r] [A1] - // * * DMIRP [A2] // mirpSide := always [A1]<--->[A2] per MT request - // * * SDPTL[R] [(B1+2)%4],[B1] - // * * SFV??[] // set freedom vector according to the 15 rules in ::AssertFVMT - // * * MDRP[mtt->Emit(buf); - - dotProd = strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y; - iStroke = Abs(dotProd) > this->cosF2; // angle between stroke and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION - if (iStroke) this->AssertStrokePhaseAngle(fvOverride,leftStationary,knot); - - A1 = !leftStationary[0]; - A2 = leftStationary[0]; - B1 = 2+!leftStationary[1]; - B2 = 2+ leftStationary[1]; - - // this->tt->AssertTTVonLine(dpv,knot[A1],knot[(A1+2)%4],this->V[knot[A1]],this->V[knot[(A1+2)%4]],true); - // this->AssertFVMT(knot[A1],knot[(A1+2)%4],knot[A2],strokeDirection); - // this->tt->MDAP(false,knot[A1]); // don't round stationary point ONLY NEEDED IF NOT TOUCHED A1 YET!!! - // this->Touched(knot[A1],this->tt->FVDir()); - fvmt = CalcDiagonalFVMT(fvOverride,knot[A1],knot[(A1+2)%4],knot[A2],strokeDirection,&refPoint0,&refPoint1); - /*****/ - if ((fvmt == fvOnY && !this->attrib[knot[A2]].touched[xRomanDir] && !this->xSmooth) || (fvmt == fvOnX && !this->attrib[knot[A2]].touched[yRomanDir] && !this->ySmooth)) { - this->tt->AssertRefPoint(0,knot[A1]); - this->tt->AssertFreeProjVector(fvmt == fvOnY ? xRomanDir : yRomanDir); - this->tt->MDRP(false,false,linkBlack,knot[A2]); - this->Touched(knot[A2],this->tt->FVDir()); - } - /*****/ - this->tt->AssertTTVonLine(dpv,knot[A1],knot[(A1+2)%4],this->V[knot[A1]],this->V[knot[(A1+2)%4]],true); - this->AssertFVMT(fvmt,refPoint0,refPoint1); - this->tt->AssertRounding(rtg); - this->tt->AssertRefPoint(0,knot[A1]); - this->tt->AssertAutoFlip(true); - this->tt->DMIRP(knot[A2],cvt,knot[A1],knot[(A1+2)%4]); - /// this->tt->MIRP(true,true,linkBlack,knot[A2],cvt,false); /// - this->Touched(knot[A2],this->tt->FVDir()); - - // this->tt->AssertTTVonLine(dpv,knot[(B1+2)%4],knot[B1],this->V[knot[(B1+2)%4]],this->V[knot[B1]],true); - // this->AssertFVMT(knot[(B1+2)%4],knot[B1],knot[B2],strokeDirection); - fvmt = CalcDiagonalFVMT(fvOverride,knot[(B1+2)%4],knot[B1],knot[B2],strokeDirection,&refPoint0,&refPoint1); - /*****/ - if ((fvmt == fvOnY && !this->attrib[knot[B2]].touched[xRomanDir] && !this->xSmooth) || (fvmt == fvOnX && !this->attrib[knot[B2]].touched[yRomanDir] && !this->ySmooth)) { - this->tt->AssertRefPoint(0,knot[B1]); - this->tt->AssertFreeProjVector(fvmt == fvOnY ? xRomanDir : yRomanDir); - this->tt->MDRP(false,false,linkBlack,knot[B2]); - this->Touched(knot[B2],this->tt->FVDir()); - } - /*****/ - this->tt->AssertTTVonLine(dpv,knot[(B1+2)%4],knot[B1],this->V[knot[(B1+2)%4]],this->V[knot[B1]],true); - this->AssertFVMT(fvmt,refPoint0,refPoint1); - // this->tt->AssertRoundingBelowPpem(rdtg,ppem); - this->tt->RoundDownToGridBelowPpem(ppem); - this->tt->AssertRefPoint(0,knot[(B2+2)%4]); - this->tt->MDRP(false,true,linkGrey,knot[B2]); - /// this->tt->AssertRoundingBelowPpem(rtg,ppem); /// - /// this->tt->AssertRefPoint(0,knot[B1]); /// - /// this->tt->MIRP(true,true,linkBlack,knot[B2],cvt,false); /// - this->Touched(knot[B2],this->tt->FVDir()); - - diagonal = new DiagParam; - for (i = 0; i < 2; i++) diagonal->leftStationary[i] = leftStationary[i]; - for (i = 0; i < 4; i++) diagonal->knot[i] = knot[i]; - this->diagonals->InsertAtEnd(diagonal); - } - } - } - *actualCvt = cvt; -} // TTSourceGenerator::Stroke - -/***** -void TTSourceGenerator::Stroke(bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[]) { - short i,lsb,mirpSide,A2,A1,B2,B1,cvtIndex[2],distance[2]; - Vector link; - RVector leftDirection,rightDirection,strokeDirection; - bool iStroke; - double dist; - int32_t infoBits; - wchar_t buf[64]; - - leftDirection = RDirectionV(this->V[knot[2]],this->V[knot[0]]); - rightDirection = RDirectionV(this->V[knot[3]],this->V[knot[1]]); - if (leftDirection.x*rightDirection.x + leftDirection.y*rightDirection.y < (ppem < 0 ? this->cosF0 : 2*this->cosF0*this->cosF0 - 1)) { // edges are "anti-parallel" || not nearly-parallel, RScalProdV(left,right);... - swprintf(error,L"cannot accept (X|Y)STROKE (edges differ by %f degrees or more)",(double)(ppem < 0 ? MAXSTROKESIDEANGLEDIFFERENCE : 2*MAXSTROKESIDEANGLEDIFFERENCE)); - } else { // general stroke "action" command - for (i = 0; i < 4; i++) this->attrib[knot[i]].dStroke = true; - strokeDirection = RAvgDirectionV(leftDirection,rightDirection); - iStroke = strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y > this->cosF2; // angle between stroke and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION - if (iStroke) { - for (i = 0; i < 4; i++) this->attrib[knot[i]].iStroke = true; - } - for (i = 0; i < 2; i++) { - link = SubV(this->V[knot[i*2+1]],this->V[knot[i*2]]); - dist = link.x*strokeDirection.y - link.y*strokeDirection.x; // project onto normal to stroke direction - distance[i] = (short)Round(dist); - } - if (this->tt) { - for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,linkDiag,cvtStroke,Abs(distance[i])) : cvt; - if (cvtIndex[0] < 0 && cvtIndex[1] < 0) - swprintf(error,L"cannot accept STROKE (no cvt number found)"); - else { - if (cvtIndex[0] < 0) cvtIndex[0] = cvtIndex[1]; - else if (cvtIndex[1] < 0) cvtIndex[1] = cvtIndex[0]; - mirpSide = Abs(distance[1]) > Abs(distance[0]) ? 1 : 0; // mirp the larger end - cvt = cvtIndex[mirpSide]; - - // for the the italic stroke phase/angle adjustment to make sense, it always looks at the first knot pair, - // regardless of whether or not this will be the mirp side further down. However, in these cases linking - // criss-cross doesn't make sense, for it would undo the effects of an adjusted italic stroke phase. - - A1 = !leftStationary[0]; - lsb = this->knots - PHANTOMPOINTS; - if (this->italicStrokePhase && iStroke) { - if (this->V[knot[A1]].y != 0) { - this->tt->AssertFreeProjVector(yItalDir); - this->tt->AssertRefPoint(0,lsb); - this->tt->ALIGNRP(knot[A1]); - this->Touched(knot[A1],yItalDir); - } - this->tt->AssertFreeProjVector(xRomanDir); // this used to be a call to fn 23, suggesting to be able to change the italic stroke - this->tt->AssertRounding(rtg); // phase globally. However, fn 23 can't be changed without changing the compiler, for - this->tt->MDAP(true,knot[A1]); // it has side effects which the compiler has to be aware of!!! - this->Touched(knot[A1],xRomanDir); - } - if (this->italicStrokeAngle && iStroke) { - this->tt->AssertFreeProjVector(xAdjItalDir); - this->tt->AssertRefPoint(0,knot[A1]); - this->tt->ALIGNRP(knot[A1+2]); - this->Touched(knot[A1+2],xAdjItalDir); - if (this->V[knot[A1]].y != 0) { - this->tt->AssertFreeProjVector(yAdjItalDir); - this->tt->AssertRefPoint(0,lsb); - this->tt->MDRP(false,false,linkGrey,knot[A1]); - this->Touched(knot[A1],yAdjItalDir); - } - } - - // leftStationary[0] := [A1]--->[A2] - // leftStationary[1] := [B1]--->[B2] - // [B1] mirpSide := either 0 for [A1]<--->[A2] or 1 for [B1]<--->[B2] depending on which has the larger cvt entry (if any at all...) - // * *** - // * [B2] to understand all combinations, it is enough to assume STROKE([A1]--->[A2],[B1]--->[B2]) with the mirpSide = [A1]<--->[A2] - // * * the other combinations are taken care of by 4 assignments below. Then, what the Stroke does, is essentially: - // * * - // * * SDPTL[R] [A1],[(A1+2)%4] - // * * SFVTPV[] // either (A2 not assumed to be at a junction) - // * * SFVTL[r] [A2],neighbour // or (A2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction - // * * MDAP[r] [A1] - // * * DMIRP [A2] - // * * SDPTL[R] [(B1+2)%4],[B1] - // * * SFVTPV[] // again, either (B2 not assumed to be at a junction) - // * * SFVTL[r] [B2],neighbour // or (B2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction - // * *[A2] MDRP[mtt->Emit(buf); - - A1 = mirpSide*2+!leftStationary[ mirpSide]; - A2 = mirpSide*2+ leftStationary[ mirpSide]; - B1 = !mirpSide*2+!leftStationary[!mirpSide]; - B2 = !mirpSide*2+ leftStationary[!mirpSide]; - - this->AssertVectorsAtPotentialJunction(dpv,knot[A1],knot[(A1+2)%4],knot[A2]); - this->tt->MDAP(false,knot[A1]); // don't round stationary point - this->Touched(knot[A1],this->tt->FVDir()); - this->tt->AssertRounding(rtg); - this->tt->AssertRefPoint(0,knot[A1]); - this->tt->AssertAutoFlip(true); - this->tt->DMIRP(knot[A2],cvt,knot[A1],knot[(A1+2)%4]); - this->Touched(knot[A2],this->tt->FVDir()); - - this->AssertVectorsAtPotentialJunction(dpv,knot[(B1+2)%4],knot[B1],knot[B2]); - this->tt->AssertRoundingBelowPpem(rdtg,ppem); - this->tt->AssertRefPoint(0,knot[(B2+2)%4]); - this->tt->MDRP(false,true,linkGrey,knot[B2]); - this->Touched(knot[B2],this->tt->FVDir()); - } - } else { - infoBits = 0; - if (this->italic && strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y > this->cosF5) { // Garamond Italic 156 ??? - if (this->charCode == 'I') infoBits = UCISTROKE; - else if (this->charCode == 'l') infoBits = LClSTROKE; // lower case 'L', that is... - else if (this->charCode == '1') infoBits = FIGSTROKE; - } - this->EnterCvt(knot[0],knot[1],(LinkColor)-1,linkDiag,cvtStroke,distance[0],true,0); - this->EnterCvt(knot[2],knot[3],(LinkColor)-1,linkDiag,cvtStroke,distance[1],true,infoBits); - } - } - *actualCvt = cvt; -} // TTSourceGenerator::Stroke -*****/ - -void TTSourceGenerator::DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[]) { - RVector strokeDirection,fixedDirection; - short i,distance[2],cvtIndex[2]; - bool xLinks,flip; - wchar_t buf[64]; - - strokeDirection = RAvgDirectionV(RDirectionV(this->V[knot[2]],this->V[knot[0]]),RDirectionV(this->V[knot[3]],this->V[knot[1]])); - xLinks = Abs(strokeDirection.x) <= Abs(strokeDirection.y); - if (leftStationary[0] != leftStationary[1]) { // Links from opposite sides - fixedDirection = RDirectionV(this->V[knot[2+!leftStationary[1]]],this->V[knot[!leftStationary[0]]]); - fixedDirection.x = Abs(fixedDirection.x); - fixedDirection.y = Abs(fixedDirection.y); - /* if we are within diagFudge of xAxis || yAxis, it seems to be better to define xLink by the fixedDirection... */ - if (fixedDirection.y < fixedDirection.x*this->tanF4 || fixedDirection.x < fixedDirection.y*this->tanF4) xLinks = fixedDirection.x <= fixedDirection.y; - } - for (i = 0; i < 2; i++) distance[i] = RectilinearDistanceOfDiagonal(xLinks,this->V[knot[i*2]],this->V[knot[i*2+1]],strokeDirection); - flip = distance[0] < 0; // the other distance doesn't seem to matter... - - if (this->tt) { - for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,xLinks ? linkX : linkY,cvtNewDiagStroke,Abs(distance[i])) : cvt; - if (cvtIndex[0] < 0 && cvtIndex[1] < 0) - swprintf(error,L"cannot accept DSTROKE (no cvt number found)"); - else { - if (cvtIndex[0] < 0) cvt = cvtIndex[1]; - else if (cvtIndex[1] < 0) cvt = cvtIndex[0]; - else if (Abs(distance[0]) < Abs(distance[1])) cvt = cvtIndex[1]; - else cvt = cvtIndex[0]; - - swprintf(buf,L"/* DStroke [%hi,%hi]%hi-[%hi,%hi]%hi */",knot[0],knot[1],distance[0],knot[2],knot[3],distance[1]); this->tt->Emit(buf); - this->tt->AssertAutoFlip(false); - this->tt->CALL656(leftStationary[0] != leftStationary[1],knot[ !leftStationary[0]],knot[ leftStationary[0]], - knot[2+!leftStationary[0]],knot[2+leftStationary[0]],cvt,ND_HEIGHT_STORE_1,xLinks,leftStationary[0] ? flip : !flip); - this->Touched(knot[ leftStationary[0]],diagDir); // if the left point is stationary, then the 0+leftStationary[0]'th point is floating... - this->Touched(knot[2+leftStationary[1]],diagDir); - } - } - *actualCvt = cvt; -} /* TTSourceGenerator::DStroke */ - -void TTSourceGenerator::IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[]) { -/***** leftStationary[] are not used for anything currently, linking is always done left-to-right, or leftStationary[0] == leftStationary[1] == true *****/ - RVector strokeDirection; - short i,distance[2],cvtIndex[2],parent,child; - bool xLinks,flip[2],move[4]; - wchar_t buf[64]; - - strokeDirection = RAvgDirectionV(RDirectionV(this->V[knot[2]],this->V[knot[0]]),RDirectionV(this->V[knot[3]],this->V[knot[1]])); - xLinks = Abs(strokeDirection.x) <= Abs(strokeDirection.y); - if (xLinks) { - for (i = 0; i < 2; i++) { - distance[i] = RectilinearDistanceOfDiagonal(xLinks,this->V[knot[i*2]],this->V[knot[i*2+1]],strokeDirection); - flip[i] = distance[i] < 0; - } - if (this->tt) { - for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,xLinks ? linkX : linkY,cvtNewDiagStroke,Abs(distance[i])) : cvt; - if (cvtIndex[0] < 0 && cvtIndex[1] < 0) - swprintf(error,L"cannot accept ISTROKE (no cvt number found)"); - else { - if (cvtIndex[0] < 0) cvt = cvtIndex[1]; - else if (cvtIndex[1] < 0) cvt = cvtIndex[0]; - else if (Abs(distance[0]) < Abs(distance[1])) cvt = cvtIndex[1]; - else cvt = cvtIndex[0]; - - swprintf(buf,L"/* IStroke [%hi,%hi]%hi-[%hi,%hi]%hi */",knot[0],knot[1],distance[0],knot[2],knot[3],distance[1]); this->tt->Emit(buf); - - for (i = 0; i < 4; i++) { - move[i] = this->attrib[knot[i]].cvt != height[i/2]; - if (move[i]) { - this->tt->CALL678(false,knot[i],knot[(i+2)%4],height[i/2],ND_ITALIC_STORE_1 + i); - this->Touched(knot[i],diagDir); - } - } - this->tt->AssertFreeProjVector(xRomanDir); - this->tt->AssertSuperRounding(1,phase,4); - this->tt->MDAP(true,knot[0]); - this->Touched(knot[0],xRomanDir); - this->tt->AssertAutoFlip(false); - this->tt->CALL64(knot[0],knot[2],RESERVED_HEIGHTSPACE_START, phase & 1,false); - for (i = 0; i <= 1; i++) { - parent = knot[i*2]; child = knot[i*2+1]; - this->tt->CALL64(parent,child,cvt,deltaRounding[this->attrib[parent].round[false]][this->attrib[child].round[false]] == rthg,flip[i]); - } - for (i = 1; i <= 3; i++) this->Touched(knot[i],xRomanDir); - for (i = 0; i < 4; i++) - if (move[i]) - this->tt->CALL678(true,knot[i],knot[(i+2)%4],height[i/2],ND_ITALIC_STORE_1 + i); - } - } - } else { - swprintf(error,L"cannot accept ISTROKE (can be used for italic strokes only)"); - } - *actualCvt = cvt; -} /* TTSourceGenerator::IStroke */ - -void TTSourceGenerator::FixDStrokes(void) { - TTVDirection fv,pv,dir[2]; - short i,j; - Attribute *attrib; - - if (this->tt) { - fv = this->tt->FVDir(); - pv = this->tt->PVDir(); - if (fv == xRomanDir && pv == xRomanDir) { - dir[0] = xRomanDir; dir[1] = yRomanDir; - } else { - dir[0] = yRomanDir; dir[1] = xRomanDir; - } - for (i = 0; i < 2; i++) { - for (j = 0; j < this->knots - PHANTOMPOINTS; j++) { - attrib = &this->attrib[j]; - if ((attrib->dStroke || attrib->iStroke) && !attrib->touched[dir[i]]) { - this->tt->AssertFreeProjVector(dir[i]); - this->tt->MDAP(false,j); - } - } - } - } -} /* TTSourceGenerator::FixDStrokes */ - -void TTSourceGenerator::Serif(bool forward, short type, short knots, short knot[], wchar_t error[]) { - short ppem[1] = {1},actualCvt; - F26Dot6 dist[1] = {one6}; - wchar_t buf[64]; - - switch (type) { - case 0: /***** - - ****[0]**** [2]**[3] - **** **** ** * - ****[1]** * - ****[6]**** * - **** ****** * - **** * - *** * - ** * - * * - * * - * * - * * - * * - * * - * * - [5]**[4] - - *****/ - if (this->tt) { - swprintf(buf,L"/* Round serif %hi %hi %hi %hi %hi %hi %hi */",knot[0],knot[1],knot[2],knot[3],knot[4],knot[5],knot[6]); this->tt->Emit(buf); - this->Link(true, false,&this->yRomanPV,false,knot[0],knot[1],cvtSerifOther,invalidCvtNum,1,ppem,dist,&actualCvt,error); - this->Link(true, false,&this->yRomanPV,false,knot[0],knot[3],cvtSerifOther,invalidCvtNum,0,NULL,NULL,&actualCvt,error); - this->Link(true, false,&this->yRomanPV,false,knot[3],knot[4],cvtSerifOther,invalidCvtNum,1,ppem,dist,&actualCvt,error); - this->Link(false,false,&this->xRomanPV,false,knot[4],knot[5],cvtSerifThin, invalidCvtNum,1,ppem,dist,&actualCvt,error); - this->Link(false,false,&this->xRomanPV,false,knot[4],knot[3],cvtSerifOther,invalidCvtNum,0,NULL,NULL,&actualCvt,error); - this->Link(false,false,&this->xRomanPV,false,knot[3],knot[2],cvtSerifThin, invalidCvtNum,1,ppem,dist,&actualCvt,error); - } - break; - case 1: /***** - - ****[3] - **** * - **** * - **** * - **** * - [2]* **** * - * *** ** * - * ** * * - [1]* * * - * * - * * - * * - * * - * * - [0] * - - - *****/ - if (this->tt) { - swprintf(buf,L"/* Triangular serif %hi %hi %hi %hi */",knot[0],knot[1],knot[2],knot[3]); this->tt->Emit(buf); - this->Link(true, false,&this->yRomanPV,false,knot[3],knot[2],cvtSerifCurveHeight,invalidCvtNum,0,NULL,NULL,&actualCvt,error); - this->Link(true, false,&this->yRomanPV,false,knot[2],knot[1],cvtSerifThin, invalidCvtNum,1,ppem,dist,&actualCvt,error); - this->Link(true, false,this->attrib[knot[0]].iStroke ? &this->yAdjItalPV : &this->yRomanPV,false,knot[2],knot[0],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error); - // set vectors to yAdjItalDir if knot[0] is part of an iStroke... - this->Link(false,false,&this->xRomanPV,false,knot[0],knot[2],cvtSerifExt, invalidCvtNum,0,NULL,NULL,&actualCvt,error); - } - break; - case 2: - case 3: /***** - - [3] * * [3] [3] * * [3] - * * * * * * * * - * * * * * * * * - * * * * * * * * - * * * * * * * * - * * * * * ** * ** - *** *** *** *** * **** * **** - [2]**** ******* ******* ****[2] [2]**** ******** ******* *****[2] - * * * * * * * * - * * * * * * * * - [1]******************************[0] [0]******************************[1] [1]******************************[0] [0]******************************[1] - - type 2, horzBase, forward type 2, horzBase, !forward type 3, horzBase, forward type 3, horzBase, !forward - - - ****[0] [2]*[1] ****[0] [2]*[1] - * * * * * * * * - * * * * * * * * - * * * * * * * * - * * * * * * * * - * * * * * * * * - *** * *** * *** * *** * - *********** * [3]******** * *********** * [3]******** * - * * * * - * * * * - * * * * - * * * * - * * * * - * * * * - [3]******** * *********** * [3]******** * *********** * - *** * *** * *** * *** * - * * * * * * * * - * * * * * * * * - * * * * * * * * - * * * * * * * * - * * * * * * * * - [2]*[1] ****[0] [2]*[1] ****[0] - - type 2, vertBase, forward type 2, vertBase, !forward type 2, italBase, forward type 2, italBase, !forward - - *****/ { - bool horzBase,vertBase,italBase; - RVector base; - double absVectProd,length; - short cvt[3],fun; - - base.x = this->V[knot[1]].x - this->V[knot[0]].x; - base.y = this->V[knot[1]].y - this->V[knot[0]].y; - length = LengthR(base); - absVectProd = base.x*this->xAxis.y - base.y*this->xAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... could do like nearHorz/Vert though... - horzBase = absVectProd < length*this->sinF3; // angle between base and xAxis < serifFudge - absVectProd = base.x*this->yAxis.y - base.y*this->yAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... and therefore need no xAxis/yAxis!!! - vertBase = absVectProd < length*this->sinF3; // angle between base and yAxis < serifFudge - absVectProd = base.x*this->slope.y - base.y*this->slope.x; absVectProd = Abs(absVectProd); // RVectProdV... - italBase = this->italic && absVectProd < length*this->sinF22; // angle between base and yAxis < 2*MAXIMUM_ITALIC_ANGLE_DEVIATION - if (vertBase || horzBase || italBase) { - if (this->tt) { - swprintf(buf,L"/* " WIDE_STR_FORMAT L" serif %hi %hi %hi */",forward ? L"Forward" : L"Backward",knot[1],knot[2],knot[3]); this->tt->Emit(buf); - if (horzBase && type == 2) { // seems to be some kind of optimisation for the frequent case of a simple serif such as in a Times 'I'... - cvt[0] = this->TheCvt(knot[3],knot[1],linkGrey,linkX,cvtSerifExt, -1); - cvt[1] = this->TheCvt(knot[1],knot[2],linkGrey,linkY,cvtSerifThin, -1); - cvt[2] = this->TheCvt(knot[1],knot[3],linkGrey,linkY,cvtSerifHeight,-1); - if (cvt[0] < 0) - swprintf(error,L"cannot accept SERIF (need cvt numbers for XLINK(%hi,%hi))",knot[3],knot[1]); - else if (cvt[1] < 0) - swprintf(error,L"cannot accept SERIF (need cvt numbers for YLINK(%hi,%hi))",knot[1],knot[2]); - else if (cvt[2] < 0) - swprintf(error,L"cannot accept SERIF (need cvt numbers for YLINK(%hi,%hi))",knot[1],knot[3]); - else { - short jumpPpemSize[1]; - F26Dot6 pixelSize[1]; - - jumpPpemSize[0] = 1; - pixelSize[0] = one6; - - this->tt->AssertMinDist(1,jumpPpemSize,pixelSize); - this->tt->AssertRounding(rtg); - this->tt->AssertAutoFlip(true); - - if (knot[2] == knot[1]+1) fun = 34; - else if (knot[2] == knot[1]-1) fun = 35; - else fun = 36; - this->tt->CALL3456(fun,knot[3],cvt[2],knot[2],cvt[1],knot[1],cvt[0]); - /* this doesn't update the knots as being touched, but since this is only relevant for STROKEs that are - neither horizontal nor vertical, and the present serif belongs to a vertical stroke, we couldn't care less... */ - } - - - } else { - if (italBase) this->Anchor(false,&this->xRomanPV,knot[1],invalidCvtNum,false,error); - this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[2],cvtSerifThin,invalidCvtNum,1,ppem,dist,&actualCvt,error); - if (type == 2) this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[3],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error); // control serif height - this->Link(!horzBase,this->attrib[knot[3]].dStroke || this->attrib[knot[3]].iStroke,!horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[3],knot[1],cvtSerifExt,invalidCvtNum,0,NULL,NULL,&actualCvt,error); - } - } - } else - swprintf(error,L"cannot accept SERIF (base differs from horizontal or vertical by %f degrees or more, or from italic angle by %f degrees or more)", - (double)serifFudge,(double)2*MAXIMUM_ITALIC_ANGLE_DEVIATION); - break; - } - case 4: /***** - - [5] * * [5] - * * * * - * * * * - * * * * - * * * * - * * * * - * * * * - **[3]******[4] ************ ************ [4]******[3]** - * * * * - [2] * * [2] - * * * * - **[1]**********************[0]** **[0]**********************[1]** - - forward !forward plus the same variations as in cases 2 and 3 - - *****/ { - bool horzBase,vertBase,italBase; - RVector base; - double absVectProd,length; - Vector /***** link21,link23,*****/link34; - - base.x = this->V[knot[1]].x - this->V[knot[0]].x; - base.y = this->V[knot[1]].y - this->V[knot[0]].y; - length = LengthR(base); - absVectProd = base.x*this->xAxis.y - base.y*this->xAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... - horzBase = absVectProd < length*this->sinF3; // angle between base and xAxis < serifFudge - absVectProd = base.x*this->yAxis.y - base.y*this->yAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... - vertBase = absVectProd < length*this->sinF3; // angle between base and yAxis < serifFudge - absVectProd = base.x*this->slope.y - base.y*this->slope.x; absVectProd = Abs(absVectProd); // RVectProdV... - italBase = this->italic && absVectProd < length*this->sinF22; // angle between base and yAxis < 2*MAXIMUM_ITALIC_ANGLE_DEVIATION - if (vertBase || horzBase || italBase) { -/* universal serif used by the new feature recognition */ - if (this->tt) { - swprintf(buf,L"/* " WIDE_STR_FORMAT L" univ-serif %hi %hi %hi %hi */",forward ? L"Forward" : L"Backward",knot[1],knot[2],knot[3],knot[4]); this->tt->Emit(buf); - - this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[3],cvtSerifThin,invalidCvtNum,1,ppem,dist,&actualCvt,error); - this->Link(!horzBase,false,!horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[4],knot[2],cvtSerifExt,invalidCvtNum,0,NULL,NULL,&actualCvt,error); - link34 = SubV(this->V[knot[3]],this->V[knot[4]]); - if (horzBase) { - if ((Abs(link34.x) > 0)) { // could compare with a small value - this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[4],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error); - } - } else { - if ((Abs(link34.y) > 0)) { - this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[4],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error); - } - } - } - } else - swprintf(error,L"cannot accept SERIF (base differs from horizontal or vertical by %f degrees or more, or from italic angle by %f degrees or more)", - (double)serifFudge,(double)2*MAXIMUM_ITALIC_ANGLE_DEVIATION); - break; - } - } -} /* TTSourceGenerator::Serif */ - -void TTSourceGenerator::Scoop(short parent0, short child, short parent1, wchar_t error[]) { - Vector base; - bool ok,y = false; - short dist,actualCvt; - - base = SubV(this->V[parent1],this->V[parent0]); - base.x = Abs(base.x); - base.y = Abs(base.y); - ok = true; - if (base.y <= base.x*this->tanF) { dist = (short)base.y; y = true; } // near horizontal - else if (base.x <= base.y*this->tanF) { dist = (short)base.x; y = false; } // near vertical - else { - ok = false; - swprintf(error,L"cannot accept SCOOP (base differs from horizontal or vertical by %f degrees or more)",(double)strokeFudge); - } - if (ok) - if (this->tt) this->Link(y,false,y ? &this->yRomanPV : &this->yRomanPV,false,parent0,child,cvtScoopDepth,invalidCvtNum,0,NULL,NULL,&actualCvt,error); -} /* TTSourceGenerator::Scoop */ - -void TTSourceGenerator::Smooth(short y, short italicFlag) { - short xyDir,contour,start,end,n,parent0,parent1,children; - TTVDirection dir; - - if (this->tt) { - if (y < 0) { // do Y and X unless legacy then do X and Y - if (this->legacyCompile) { - this->Smooth(0, italicFlag); - this->Smooth(1, -1); - }else { - this->Smooth(1, -1); - this->Smooth(0, italicFlag); - } - } else { // Y or X only - if (italicFlag < 0) { - this->tt->IUP(y == 1); - } else { - - xyDir = y == 0 ? xRomanDir : yRomanDir; - dir = y == 0 ? (italicFlag == 0 ? xItalDir : xAdjItalDir) : (italicFlag == 0 ? yItalDir : yAdjItalDir); - - this->tt->AssertFreeProjVector(dir); - - for (contour = 0; contour < this->glyph->numContoursInGlyph; contour++) { - start = this->glyph->startPoint[contour]; - end = this->glyph->endPoint[contour]; - n = (end - start + 1); - for (parent0 = start; parent0 <= end && !this->attrib[parent0].touched[xyDir]; parent0++); - while (parent0 <= end) { // => this->attrib[parent0].touched[xyDir] - parent1 = (parent0-start+1)%n + start; - children = 0; - while (!this->attrib[parent1].touched[xyDir]) { - children++; - parent1 = (parent1-start+1)%n + start; - } - if (parent0 != parent1 && children > 0) { - this->tt->IPRange(y == 1,parent0,parent1,start,end); - } - parent0 = parent0 + children + 1; - } - } - } - if (y == 1) - this->ySmooth = true; - else - this->xSmooth = true; - } - } -} /* TTSourceGenerator::Smooth */ - -void TTSourceGenerator::VacuFormLimit(short ppem) { - this->vacuFormLimit = ppem; -} /* TTSourceGenerator::VacuFormLimit */ - -void TTSourceGenerator::VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[]) { - short i,cvt; - VacuFormParams *vacu; - - if (this->tt) { - // if (this->font->controlRounds || this->font->onlyControlLCBranches && this->charGroup == lowerCase && type == 2) { - if (this->vacuForms < maxVacuForms) { - cvt = this->TheCvt(-1,-1,linkGrey,linkAnyDir,type == 2 ? cvtBranchBendRadius : cvtBendRadius,radius); - if (cvt >= 0) { - vacu = &this->vacuForm[this->vacuForms]; - vacu->type = type; - vacu->radius = radius; - vacu->cvt = cvt; - for (i = 0; i < 2; i++) vacu->forward[i] = forward[i]; - for (i = 0; i < 4; i++) vacu->knot[i] = knot[i]; - this->vacuForms++; - } else - swprintf(error,L"cannot accept VACUFORMROUND (no cvt number found)"); // if we have to test this at all!!! - } else - swprintf(error,L"too many VACUFORMROUND commands (cannot have more than %li VACUFORMROUNDs)",maxVacuForms); - // } - } -} /* TTSourceGenerator::VacuFormRound */ - -void TTSourceGenerator::Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[]) { - if (this->tt) { - this->tt->AssertFreeProjVector(y ? yRomanDir : xRomanDir); // no [adjusted] italic directions... - this->tt->DLT(false,color,knot,amount,ppemSize); - } -} /* TTSourceGenerator::Delta */ - -void TTSourceGenerator::Call(short actParams, short anyNum[], short functNum) { - if (this->tt) this->tt->CALL(actParams,anyNum,functNum); -} /* TTSourceGenerator::Call */ - -void TTSourceGenerator::Asm(bool inLine, wchar_t text[], wchar_t error[]) { - if (this->tt) this->tt->ASM(inLine,text); -} /* TTSourceGenerator::Asm */ - -void TTSourceGenerator::Quit(void) { - // nix -} /* TTSourceGenerator::Quit */ - -void InitFreeProjVector(TTVDirection pv, ProjFreeVector *projFreeVector) { - short i; - - projFreeVector->pv.dir = pv; - projFreeVector->pv.from = projFreeVector->pv.to = invalidKnotNum; - for (i = 0; i < maxParams; i++) { - projFreeVector->fv[i].dir = pv; - projFreeVector->fv[i].from = projFreeVector->fv[i].to = invalidKnotNum; - } -} // InitFreeProjVector - -void TTSourceGenerator::InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError) { - short i,j,n,cont; - double vectProd,deg; - Attribute *attrib; - Vector V[3],D[2]; - RVector T[2]; - wchar_t dateTime[32],buf[128]; - - this->legacyCompile = legacyCompile; - - InitFreeProjVector(xRomanDir, &this->xRomanPV); - InitFreeProjVector(yRomanDir, &this->yRomanPV); - InitFreeProjVector(xItalDir, &this->xItalPV); - InitFreeProjVector(yItalDir, &this->yItalPV); - InitFreeProjVector(xAdjItalDir,&this->xAdjItalPV); - InitFreeProjVector(yAdjItalDir,&this->yAdjItalPV); - - this->xAxis.x = 1; this->yAxis.x = 0; this->slope.x = 0; - this->xAxis.y = 0; this->yAxis.y = 1; this->slope.y = 1; - this->riseCvt = this->runCvt = invalidCvtNum; - this->cosF = cos(Rad(strokeFudge)); - this->tanF = tan(Rad(strokeFudge)); - this->cosF0 = cos(Rad(MAXSTROKESIDEANGLEDIFFERENCE)); - this->cosF1 = cos(Rad(ALIGN_TOLERANCE)); // more fudge factors... - this->cosF2 = cos(Rad(MAXIMUM_ITALIC_ANGLE_DEVIATION)); - this->sinF22 = sin(Rad(2*MAXIMUM_ITALIC_ANGLE_DEVIATION)); - this->sinF3 = sin(Rad(serifFudge)); - this->tanF4 = tan(Rad(diagFudge)); - this->cosF5 = cos(Rad(gmndItFudge)); - this->cosF6 = cos(Rad(neighFudge)); - this->cosMT = cos(Rad(diagFudgeMT)); - this->tanMT = tan(Rad(diagFudgeMT)); - - this->font = font; - this->glyph = glyph; - this->glyphIndex = glyphIndex; this->charCode = this->font->CharCodeOf(this->glyphIndex); - - this->charGroup = this->font->CharGroupOf(this->glyphIndex); - - this->knots = (glyph->numContoursInGlyph ? glyph->endPoint[glyph->numContoursInGlyph - 1] + 1 : 0) + PHANTOMPOINTS; - - this->attrib = (AttributePtr)NewP(this->knots*sizeof(Attribute)); - *memError = this->attrib == NULL; - this->V = (VectorPtr)NewP(this->knots*sizeof(Vector)); - *memError = *memError || this->V == NULL; - this->diagonals = new LinearListStruct; - this->aligns = new LinearListStruct; - *memError = *memError || this->diagonals == NULL || this->aligns == NULL; - if (*memError) return; - - this->vacuFormLimit = -1; - this->vacuForms = 0; - - this->tt = tt; - - for (i = 0; i < this->knots; i++) { - this->V[i].x = glyph->x[i]; - this->V[i].y = glyph->y[i]; - } - - for (cont = 0; cont < glyph->numContoursInGlyph; cont++) { - i = glyph->startPoint[cont]; - n = glyph->endPoint[cont] - i + 1; - V[0] = this->V[i+n-1]; V[1] = this->V[i]; D[0] = SubV(V[1],V[0]); T[0] = RDirectionV(V[1],V[0]); - for (j = 0; j < n; j++) { - V[2] = this->V[i + (j + 1)%n]; D[1] = SubV(V[2],V[1]); T[1] = RDirectionV(V[2],V[1]); - vectProd = T[0].x*T[1].y - T[0].y*T[1].x; - vectProd = Max(-1,Min(vectProd,1)); // force asin's arg into [-1..1] - deg = Deg(asin(vectProd)); - attrib = &this->attrib[i + j]; - attrib->deltaAngle = (short)Round(deg); - V[0] = V[1]; V[1] = V[2]; T[0] = T[1]; D[0] = D[1]; - } - } - - this->InitCodePathState(); - - if (this->tt) { - DateTimeStrg(dateTime); - swprintf(buf,L"/* TT glyph %i, char 0x%lx",this->glyphIndex,this->charCode); - if (L' ' < this->charCode && this->charCode < 0x7f) { swprintf(&buf[STRLENW(buf)],L" (%c)",(wchar_t)this->charCode); } - swprintf(&buf[STRLENW(buf)],L" */"); - this->tt->Emit(buf); - swprintf(buf,L"/* VTT " WIDE_STR_FORMAT L" compiler " WIDE_STR_FORMAT L" */",VTTVersionString,dateTime); this->tt->Emit(buf); - } -} /* TTSourceGenerator::InitTTGenerator */ - -void TTSourceGenerator::InitCodePathState(void) { - short i,j; - Attribute *attrib; - - this->italic = false; - this->mainStrokeAngle = this->glyphStrokeAngle = this->setItalicStrokePhase = this->setItalicStrokeAngle = false; - this->xSmooth = this->ySmooth = false; - this->leftAnchor = this->rightAnchor = -1; - - for (i = 0; i < this->knots; i++) { - attrib = &this->attrib[i]; - for (j = 0; j < 2; j++) attrib->round[j] = rtg; // default - attrib->cvt = invalidCvtNum; - for (j = 0; j < 2; j++) attrib->touched[j] = false; - attrib->dStroke = false; - attrib->iStroke = false; - attrib->on = glyph->onCurve[i]; - attrib->vacu = false; - } -} // TTSourceGenerator::InitCodePathState - -void TTSourceGenerator::TermCodePathState(void) { - if (this->tt) this->DoVacuFormRound(); -} // TTSourceGenerator::TermCodePathState - -void TTSourceGenerator::TermTTGenerator(void) { - this->TermCodePathState(); - if (this->aligns) delete this->aligns; - if (this->diagonals) delete this->diagonals; - if (this->V) DisposeP((void**)&this->V); - if (this->attrib) DisposeP((void**)&this->attrib); -} /* TTSourceGenerator::TermTTGenerator */ - -TTSourceGenerator::TTSourceGenerator(void) { - /* nix */ -} /* TTSourceGenerator::TTSourceGenerator */ - -TTSourceGenerator::~TTSourceGenerator(void) { - /* nix */ -} /* TTSourceGenerator::~TTSourceGenerator */ - -void TTSourceGenerator::AssertFreeProjVector(const TTVectorDesc *pv, const TTVectorDesc *fv) { - if (pv->dir < diagDir && fv->dir < diagDir && pv->dir == fv->dir) - this->tt->AssertFreeProjVector(pv->dir); - else if (fv->dir == yItalDir || fv->dir == yAdjItalDir) { // first assert fv because fn call overwrites pv too... - this->tt->AssertFreeProjVector(fv->dir); - if (pv->dir < diagDir) { - this->tt->AssertPVonCA(pv->dir == yRomanDir); - } else { - this->tt->AssertTTVonLine(dpv,pv->from,pv->to,this->V[pv->from],this->V[pv->to],pv->dir == perpDiagDir); - } - } else if (pv->dir == xItalDir || pv->dir == xAdjItalDir) { // first assert pv because fn call overwrites fv too... - this->tt->AssertFreeProjVector(pv->dir); - if (fv->dir < diagDir) { - this->tt->AssertFVonCA(fv->dir == yRomanDir); - } else { - this->tt->AssertTTVonLine(::fv,fv->from,fv->to,this->V[fv->from],this->V[fv->to],fv->dir == perpDiagDir); - } - } else { - if (pv->dir < diagDir) { - this->tt->AssertPVonCA(pv->dir == yRomanDir); - } else { - this->tt->AssertTTVonLine(dpv,pv->from,pv->to,this->V[pv->from],this->V[pv->to],pv->dir == perpDiagDir); - } - if (fv->dir < diagDir) { - this->tt->AssertFVonCA(fv->dir == yRomanDir); - } else { - this->tt->AssertTTVonLine(::fv,fv->from,fv->to,this->V[fv->from],this->V[fv->to],fv->dir == perpDiagDir); - } - } -} // TTSourceGenerator::AssertFreeProjVector - -void TTSourceGenerator::CondRoundInterpolees(bool y, short children, short child[], Rounding actual[], Rounding targeted) { - short i,knot; - bool negativeDist; - - for (i = 0; i < children; i++) { - if (actual[i] == targeted) { - // same roundabout way as in Link to maintain philosophy that knot gets rounded, not (absolute or relative) distance - knot = child[i]; - negativeDist = y ? this->V[knot].y < 0 : this->V[knot].x < 0; - if (negativeDist && rdtg <= targeted && targeted <= rutg) targeted = (Rounding)(((short)targeted - (short)rdtg + 1)%2 + (short)rdtg); // rdtg <=> rutg - - this->tt->AssertRounding(targeted); // this will set the rounding method only once per targeted rounding method, if once at all... - this->tt->MDAP(true,knot); - } - } -} /* TTSourceGenerator::CondRoundInterpolees */ - -short TTSourceGenerator::Neighbour(short parent0, short parent1, short child, bool immediate) { - short cont,base,n,inc,dec,pred,succ,temp; - RVector predD,succD,tempD,parentD; - double predP,succP; - - /***** - - along contour, pick child's neighbours (one of the parents or the neighbour we're looking for) - and select the one with larger angle to "parent line". Notice that the neighbour may actually - be the neighbour's neighbour, if it is too close, and will be taken from neighbour's neighbour - along the contour as long as it is not too far away and its angle is within neighFudge degrees... - The problem here seems to be that we would like to set the FV onto the line [neighbour]-[child], - but these two may not be where they used to be due to previous instructions, so we attempt to - find a neighbour which is far enough on a roughly straight line in order to define the freedom - direction reasonnably... What I am tempted to demand here is a Dual Freedom Vector that much like - the Dual Projection Vector uses the coordinates of the original outline, rather than the grid- - fitted one. Hm... - - [parent1] - * - * - * - * - * - [child]**********[neighbour] maybe this situation - - - [child]**********[neighbour] or this one - * - * - * - * - * - [parent0] - - *****/ - - for (cont = 0; this->glyph->endPoint[cont] < child; cont++); - base = this->glyph->startPoint[cont]; n = this->glyph->endPoint[cont] - base + 1; inc = 1; dec = n - 1; - - pred = Next(child,base,n,dec); - if (!immediate && DistV(this->V[child],this->V[pred]) < this->emHeight/100) pred = Next(pred,base,n,dec); - predD = RDirectionV(this->V[pred],this->V[child]); - if (!immediate) { // try to get a "bigger" picture - temp = Next(pred,base,n,dec); tempD = RDirectionV(this->V[temp],this->V[child]); - while (DistV(this->V[child],this->V[pred]) < this->emHeight/12 && predD.x*tempD.x + predD.y*tempD.y >= this->cosF6) { - pred = temp; predD = tempD; - temp = Next(pred,base,n,dec); tempD = RDirectionV(this->V[temp],this->V[child]); - } - } - - succ = Next(child,base,n,inc); - if (!immediate && DistV(this->V[child],this->V[succ]) < this->emHeight/100) succ = Next(succ,base,n,inc); - succD = RDirectionV(this->V[succ],this->V[child]); - if (!immediate) { // try to get a "bigger" picture - temp = Next(succ,base,n,inc); tempD = RDirectionV(this->V[temp],this->V[child]); - while (DistV(this->V[child],this->V[succ]) < this->emHeight/12 && succD.x*tempD.x + succD.y*tempD.y >= this->cosF6) { - succ = temp; succD = tempD; - temp = Next(succ,base,n,inc); tempD = RDirectionV(this->V[temp],this->V[child]); - } - } - - parentD = RDirectionV(this->V[parent1],this->V[parent0]); - predP = VectProdRV(predD,parentD); - succP = VectProdRV(succD,parentD); - return Abs(predP) > Abs(succP) ? pred : succ; -} // TTSourceGenerator::Neighbour - -void TTSourceGenerator::AssertVectorsAtPotentialJunction(TTVector pv, short parent0, short parent1, short child) { // PV perpendicular to line to which to align - // pv is either the real pv or the dual pv - short deltaAngle,neighbour; - - this->tt->AssertTTVonLine(pv,parent0,parent1,this->V[parent0],this->V[parent1],true); - deltaAngle = this->attrib[child].deltaAngle; - if (-noJunction <= deltaAngle && deltaAngle <= +noJunction) { // we make a turn within ±noJunction° => align "perpendicularly" to "parent line" - // for CompilerLogic(1) this used to choose among x-axis or y-axis, whichever is closer to the parent line... - this->tt->AssertFVonPV(); - } else { // assume it is a junction of some kind, align "parallel" to joining line - neighbour = this->Neighbour(parent0,parent1,child,false); - this->tt->AssertTTVonLine(fv,child,neighbour,this->V[child],this->V[neighbour],false); - } -} /* TTSourceGenerator::AssertVectorsAtPotentialJunction */ - -FVMTDirection TTSourceGenerator::CalcDiagonalFVMT(FVOverride fv, short parent0, short parent1, short child, RVector strokeDirection, short *refPoint0, short *refPoint1) { - Attribute *attrib; - ListElem *diag = nullptr,*align = nullptr; - DiagParam *diagParam = nullptr; - AlignParam *alignParam = nullptr; - bool previousStroke,previousAlign; - short neighbour = this->Neighbour(parent0,parent1,child,true); - // short deltaAngle = this->attrib[child].deltaAngle; - short diagChild = 0,alignChild,otherChild; - Vector D = SubV(this->V[neighbour],this->V[child]); - FVMTDirection fvmt; - - attrib = &this->attrib[child]; - - for (diag = this->diagonals->first, previousStroke = false; diag && !previousStroke; diag = diag->next) { - diagParam = (DiagParam *)diag; - for (diagChild = 0; diagChild < 4 && diagParam->knot[diagChild] != child; diagChild++); - previousStroke = diagChild < 4; - } - for (align = this->aligns->first, previousAlign = false; align && !previousAlign; align = align->next) { - alignParam = (AlignParam *)align; - for (alignChild = 0; alignChild < alignParam->children && alignParam->child[alignChild] != child; alignChild++); - previousAlign = alignChild < alignParam->children; - } - - if (this->xSmooth && this->ySmooth) { // follow rule set #2 - - if (ScalProdRV(this->slope,strokeDirection) >= this->cosMT) { // rule #1: diagonal is within ±5° of main stroke angle - fvmt = fvOnX; - } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #2: neighbour within ±5° of x-axis - fvmt = fvOnX; - } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #3: neighbour within ±5° of y-axis - fvmt = fvOnY; - } else { // rule #4: kitchen sink case - fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = neighbour; - } - - } else { // follow rule set #1 - // for the first two cases, make a more or less logical decision - if (previousStroke) { // rule #1: child has already been involved in a [diagonal] stroke (more than two strokes would be difficult...) - otherChild = diagParam->knot[(diagChild + 2)%4]; - fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = otherChild; - } else if (previousAlign) { // rule #1B: child has already been involved in an align - // such as when doing the lower leg of an Arial K after aligning the junction points with the lower edge of the upper leg - fvmt = fvOnLine; *refPoint0 = alignParam->parent0; *refPoint1 = alignParam->parent1; - // for all the following cases, make an educated guess - } else if (ScalProdRV(this->slope,strokeDirection) >= this->cosMT) { // rule #2: diagonal is within ±5° of main stroke angle - fvmt = fvOnX; - } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #3: neighbour within ±5° of x-axis - fvmt = fvOnX; // fv = x - } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #4: neighbour within ±5° of y-axis - fvmt = fvOnY; // fv = y - } else if (!attrib->on && D.x == 0 && this->attrib[neighbour].touched[xRomanDir]) { // rule #5: preserve tangent property of x-extremum - fvmt = fvOnY; // dx == 0 => fv can only be y - } else if (!attrib->on && D.y == 0 && this->attrib[neighbour].touched[yRomanDir]) { // rule #6: preserve tangent property of y-extremum - fvmt = fvOnX; // dy == 0 => fv can only be x - } else if (this->xSmooth || this->ySmooth) { // rules #7,8: all x or y are fixed, can only move in y or x respectively - fvmt = this->xSmooth ? fvOnY : fvOnX; - // } else if (Abs(strokeDirection.x) != Abs(strokeDirection.y)) { // rules #9,10: kitchen sink case (all other rules haven't applied), diagonal is within ±45° of x or y-axis - } else { - fvmt = Abs(strokeDirection.x) > Abs(strokeDirection.y) ? fvOnY : fvOnX; - // } else { // rule #11: 45°-case of kitchen sink case... - // fvmt = fvOnPV; - } - } - return fvmt; -} // TTSourceGenerator::CalcDiagonalFVMT - -FVMTDirection TTSourceGenerator::CalcAlignFVMT(FVOverride fv, short parent0, short parent1, short child, RVector alignDirection, short *refPoint0, short *refPoint1) { - Attribute *attrib = &this->attrib[child]; - ListElem *diag = nullptr,*align = nullptr; - DiagParam *diagParam = nullptr; - AlignParam *alignParam = nullptr; - bool previousStroke,previousAlign; - short neighbour = this->Neighbour(parent0,parent1,child,true); - short deltaAngle = attrib->deltaAngle; - short diagChild = 0,alignChild,otherChild; - Vector D = SubV(this->V[neighbour],this->V[child]); - FVMTDirection fvmt; - - for (diag = this->diagonals->first, previousStroke = false; diag && !previousStroke; diag = diag->next) { - diagParam = (DiagParam *)diag; - for (diagChild = 0; diagChild < 4 && diagParam->knot[diagChild] != child; diagChild++); - previousStroke = diagChild < 4; - } - for (align = this->aligns->first, previousAlign = false; align && !previousAlign; align = align->next) { - alignParam = (AlignParam *)align; - for (alignChild = 0; alignChild < alignParam->children && alignParam->child[alignChild] != child; alignChild++); - previousAlign = alignChild < alignParam->children; - } - - if (this->xSmooth && this->ySmooth) { // follow rule set #2 - if (ScalProdRV(this->slope,alignDirection) >= this->cosMT) { // rule #1: diagonal is within ±5° of main stroke angle - fvmt = fvOnX; - } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #2: neighbour within ±5° of x-axis - fvmt = fvOnX; - } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #3: neighbour within ±5° of y-axis - fvmt = fvOnY; - } else if (-noJunction <= deltaAngle && deltaAngle <= +noJunction) { // rule #4a: don't put fv on line to neighbor... - fvmt = fvOnPV; - } else { // ...unless assumed to be a junction - fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = neighbour; - } - } else { // follow modified rule set #1 - // for the first four cases, make a more or less logical decision - if (previousStroke) { // rule #1: child has already been involved in a [diagonal] stroke (more than two strokes would be difficult...) - otherChild = diagParam->knot[(diagChild + 2)%4]; - fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = otherChild; - } else if (previousAlign) { // rule #1B: child has already been involved in an align - fvmt = fvOnLine; *refPoint0 = alignParam->parent0; *refPoint1 = alignParam->parent1; - } else if (fv == fvForceX) { - fvmt = fvOnX; - } else if (fv == fvForceY) { - fvmt = fvOnY; - // for all the following cases, make an educated guess - } else if (attrib->touched[xRomanDir] != attrib->touched[yRomanDir]) { // x xor y are fixed, can only move in y or x respectively - fvmt = attrib->touched[xRomanDir] ? fvOnY : fvOnX; - } else if (this->xSmooth != this->ySmooth) { // rules #7,8: all x or y are fixed, can only move in y or x respectively - fvmt = this->xSmooth ? fvOnY : fvOnX; - } else if (ScalProdRV(this->slope,alignDirection) >= this->cosMT) { // rule #2: diagonal is within ±5° of main stroke angle - fvmt = fvOnX; - } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #3: neighbour within ±5° of x-axis - fvmt = fvOnX; // fv = x - } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #4: neighbour within ±5° of y-axis - fvmt = fvOnY; // fv = y -// } else if (!attrib->on && D.x == 0 && this->attrib[neighbour].touched[xRomanDir]) { // rule #5: preserve tangent property of x-extremum -// fvmt = fvOnY; // dx == 0 => fv can only be y -// } else if (!attrib->on && D.y == 0 && this->attrib[neighbour].touched[yRomanDir]) { // rule #6: preserve tangent property of y-extremum -// fvmt = fvOnX; // dy == 0 => fv can only be x -// } else if (Abs(alignDirection.x) != Abs(alignDirection.y)) { // rules #9,10: kitchen sink case (all other rules haven't applied), diagonal is within ±45° of x or y-axis -// } else { -// fvmt = Abs(alignDirection.x) > Abs(alignDirection.y) ? fvOnY : fvOnX; - } else { // rule #11: 45°-case of kitchen sink case... - fvmt = fvOnPV; - } - } - return fvmt; -} // TTSourceGenerator::CalcAlignFVMT - -void TTSourceGenerator::AssertFVMT(FVMTDirection fvmt, short point0, short point1) { - switch (fvmt) { - case fvOnX: this->tt->AssertFVonCA(false); break; - case fvOnY: this->tt->AssertFVonCA(true); break; - case fvOnPV: this->tt->AssertFVonPV(); break; - case fvOnLine: this->tt->AssertTTVonLine(fv,point0,point1,this->V[point0],this->V[point1],false); break; - } -} // TTSourceGenerator::AssertFVMT - -void TTSourceGenerator::Touched(short knot, TTVDirection dir) { - bool *touched = &this->attrib[knot].touched[0]; -// short i; - -// xRomanDir = 0, yRomanDir, xItalDir, yItalDir, xAdjItalDir, yAdjItalDir, diagDir, perpDiagDir -// for italic angles in x, pv is perpendicular to the italic angle, while fv is x, hence touches x only -// for italic angles in y, pv is y, while fv is parallel to the italic angle, hence touches x and y - touched[xRomanDir] = touched[xRomanDir] || dir == xRomanDir || dir >= xItalDir; - touched[yRomanDir] = touched[yRomanDir] || dir == yRomanDir || dir == yItalDir || dir == yAdjItalDir || dir >= diagDir; -// for (i = xRomanDir; i < xItalDir; i++) touched[i] |= dir == i || dir >= xItalDir; -} /* TTSourceGenerator::Touched */ - -short TTSourceGenerator::TheCvt(short parent, short child, LinkColor color, LinkDirection direction, CvtCategory category, short distance) { - Vector D; - - if (color < 0) color = this->glyph->TheColor(parent,child); // provide default color - if (category < 0) category = cvtDistance; // provide default category - if (distance < 0) { // provide default distance - D = SubV(this->V[parent],this->V[child]); - distance = direction == linkY ? (short)D.x : (short)D.y; - distance = Abs(distance); - } - return (short)this->font->TheCvt()->GetBestCvtMatch(this->charGroup,color,direction,category,distance); -} /* TTSourceGenerator::TheCvt */ - -/***** The VacuFormRound code still contains some Sampo-like convolutions... - But before attempting to improve this, it would be worthwhile re-thinking the concept - of VacuFormRound in general: what do we want it to achieve, and how do we achieve this. - Hopefully, this reduces the many different functions necessary to implement VacuForm- - Round, and therefore makes the overall code simpler, not needing that many decisions, - which function is to be called depending on what VacuFormRound type and configuration - of knots... For the curious: cf. fdefs0.c *****/ -void TTSourceGenerator::DoVacuFormRound(void) { - short pass,labelNumber,labels = 0,i,j,first,last,lo,hi,l,h,type,x1,x2,y1,y2,nextX1,nextX2,nextY1,nextY2,base1,n1,inc1,dec1,base2,n2,inc2,dec2,fun,knots,knot[maxVacuForms],next,dist; - VacuFormParams *vacu; - wchar_t buf[32]; - - for (first = 0; first < this->vacuForms && this->vacuForm[first].type == 2; first++); // type 2 vacuforms need a guard for each instance, since the guard measures a distance, - for (last = this->vacuForms-1; last >= 0 && this->vacuForm[last].type == 2; last--); // while all other types get by on one single guard which depends on a ppem size. - - if (this->vacuFormLimit >= 0) { - labels = 1; - for (i = 0; i < this->vacuForms; i++) if (this->vacuForm[i].type == 2) labels++; - this->tt->Emit(L"MPPEM[]"); - swprintf(buf,L"GT[], %hi, *",this->vacuFormLimit); this->tt->Emit(buf); - swprintf(buf,L"JROF[], #LRnd%hi, *",labels); this->tt->Emit(buf); // branch if vacuFormLimit > ppem... - this->tt->Emit(L"#BEGIN"); - } - labelNumber = 0; - for (pass = 0; pass <= 3; pass++) { - - for (i = 0; i < this->vacuForms; i++) { - - vacu = &this->vacuForm[i]; - type = vacu->type; - - if ((pass < 3) == (type != 2)) { // in passes 0 through 2 do all types != 2, and in pass 3 do type 2 - - x1 = vacu->knot[0]; this->attrib[x1].vacu = true; // start points may be off-curve points - x2 = vacu->knot[1]; this->attrib[x2].vacu = true; // hence include them into the candidates - y1 = vacu->knot[2]; this->attrib[y1].vacu = true; // for points to be flipped to on-curve - y2 = vacu->knot[3]; this->attrib[y2].vacu = true; // upon completing passes 2 and 3... - - if ((pass == 0 && i == first) || pass == 3) { - if (pass == 3) { // type == 2 - this->tt->AssertFreeProjVector(yRomanDir); - if (this->glyph->y[y2] > this->glyph->y[y1]) swprintf(buf,L"MD[N], %hi, %hi",y2,y1); - else swprintf(buf,L"MD[N], %hi, %hi",y1,y2); - this->tt->Emit(buf); - swprintf(buf,L"EQ[], 64, *"); this->tt->Emit(buf); - } else { - swprintf(buf,L"RS[], %hi",MAIN_STROKE_IS_ONE_PIXEL_STORAGE); this->tt->Emit(buf); - } - swprintf(buf,L"JROF[], #LRnd%hi, *",labelNumber); this->tt->Emit(buf); - this->tt->Emit(L"#BEGIN"); - } - - for (j = 0; x1 > this->glyph->endPoint[j]; j++); - base1 = this->glyph->startPoint[j]; n1 = this->glyph->endPoint[j] - base1 + 1; - if (vacu->forward[0]) { inc1 = 1; dec1 = n1 - 1; } else { inc1 = n1 - 1; dec1 = 1; } - - for (j = 0; x2 > this->glyph->endPoint[j]; j++); - base2 = this->glyph->startPoint[j]; n2 = this->glyph->endPoint[j] - base2 + 1; - if (vacu->forward[1]) { inc2 = 1; dec2 = n2 - 1; } else { inc2 = n2 - 1; dec2 = 1; } - - nextX1 = Next(x1,base1,n1,inc1); this->attrib[nextX1].vacu = true; - nextX2 = Next(x2,base2,n2,inc2); this->attrib[nextX2].vacu = true; - nextY1 = Next(y1,base1,n1,dec1); this->attrib[nextY1].vacu = true; - nextY2 = Next(y2,base2,n2,dec2); this->attrib[nextY2].vacu = true; - - if (pass == 1 || pass == 3) { // move horizontal tangent points to make tangent comply with radius (defined by cvt) - this->tt->AssertFreeProjVector(xRomanDir); - dist = this->glyph->x[x1]-this->glyph->x[nextX1]; - if (Abs(dist) > vacuFudge(this->font)) { - this->tt->AssertRefPoint(0,x1); - this->tt->ALIGNRP(nextX1); - } - dist = this->glyph->x[x2]-this->glyph->x[nextX2]; - if (Abs(dist) > vacuFudge(this->font)) { - this->tt->AssertRefPoint(0,x2); - this->tt->ALIGNRP(nextX2); - } - if (this->glyph->x[x1] < this->glyph->x[y1]) { // with the axis - fun = type == 3 ? 2 : 0; - this->tt->CALL012345(fun,nextY2,nextY1,x2,vacu->cvt); - } else { // against the axis - fun = type == 3 ? 3 : 1; - if (type == 2) this->tt->CALL012345(fun,nextY2,nextY1,x2,vacu->cvt); - else this->tt->CALL012345(fun,nextY1,nextY2,x1,vacu->cvt); - } - } - - if (pass == 0 || pass == 3) { // move vertical tangent points to make tangent comply with radius (defined by cvt) - this->tt->AssertFreeProjVector(yRomanDir); - dist = this->glyph->y[y1]-this->glyph->y[nextY1]; - if (Abs(dist) > vacuFudge(this->font)) { - this->tt->AssertRefPoint(0,y1); - this->tt->ALIGNRP(nextY1); - } - dist = this->glyph->y[y2]-this->glyph->y[nextY2]; - if (Abs(dist) > vacuFudge(this->font)) { - this->tt->AssertRefPoint(0,y2); - this->tt->ALIGNRP(nextY2); - } - if (type == 2) { - fun = this->glyph->y[x1] > this->glyph->y[y1] ? 4 : 5; // with the axis and against the axis - this->tt->CALL012345(fun,nextX2,nextX1,y2,vacu->cvt); - } else { - if (type == 4) { - fun = this->glyph->y[x1] > this->glyph->y[y1] ? 2 : 3; // with the axis and against the axis - } else { // type == 1 - fun = this->glyph->y[x1] > this->glyph->y[y1] ? 0 : 1; // with the axis and against the axis - } - if (this->glyph->x[x1] < this->glyph->x[y1]) this->tt->CALL012345(fun,nextX2,nextX1,y2,vacu->cvt); - else this->tt->CALL012345(fun,nextX1,nextX2,y1,vacu->cvt); - } - } - - if (pass == 2 || pass == 3) { // move remaining points "diagonally" to (vertical) tangent points - if (nextX1 != nextY1 && nextX1 != y1 && nextY1 != x1) { // inner contour (or outer) - knots = 0; next = Next(nextX1,base1,n1,inc1); - while (knots < maxVacuForms && next != nextY1) { - knot[knots] = next; knots++; - next = Next(next,base1,n1,inc1); - } - if (knots > 1) - this->tt->CALL6(knots,knot,nextX1); - else if (knots == 1) { - if (nextX1 == knot[0] + 1) this->tt->CALL378(37,knot[0]); // special case of function 6 for nextX1 = knot[0] + 1... - else if (nextX1 == knot[0] - 1) this->tt->CALL378(38,knot[0]); // special case of function 6 for nextX1 = knot[0] - 1... - else this->tt->CALL6(knots,knot,nextX1); - } - for (j = 0; j < knots; j++) this->attrib[knot[j]].vacu = true; - } - if (nextX2 != nextY2 && nextX2 != y2 && nextY2 != x2) { // outer contour (or inner) - knots = 0; next = Next(nextX2,base2,n2,inc2); - while (knots < maxVacuForms && next != nextY2) { - knot[knots] = next; knots++; - next = Next(next,base2,n2,inc2); - } - if (knots > 1) - this->tt->CALL6(knots,knot,nextX2); - else if (knots == 1) { - if (nextX2 == knot[0] + 1) this->tt->CALL378(37,knot[0]); // special case of function 6 for nextX2 = knot[0] + 1... - else if (nextX2 == knot[0] - 1) this->tt->CALL378(38,knot[0]); // special case of function 6 for nextX2 = knot[0] - 1... - else this->tt->CALL6(knots,knot,nextX2); - } - for (j = 0; j < knots; j++) this->attrib[knot[j]].vacu = true; - } - } - - if ((pass == 2 && i == last) || pass == 3) { - lo = 0; - while (lo < this->knots) { // determine a range of (off-curve) points that have been moved "diagonally" (vacu flag == true) - while (lo < this->knots && !(this->attrib[lo].vacu || this->glyph->onCurve[lo])) lo++; - hi = lo; - // optimise for code size: include all points which are vacu or on-curve - while (hi+1 < this->knots && (this->attrib[hi+1].vacu || this->glyph->onCurve[hi+1])) hi++; - // optimise for speed: exclude any on-curve points on the fringe of the interval - for (l = lo; l <= hi && this->glyph->onCurve[l]; l++); - for (h = hi; h >= lo && this->glyph->onCurve[h]; h--); - if (h > l) swprintf(buf,L"FLIPRGON[], %hi, %hi",l,h); else if (h == l) swprintf(buf,L"FLIPPT[], %hi",l); - if (h >= l) this->tt->Emit(buf); - lo = hi+1; - } - this->tt->Emit(L"#END"); - swprintf(buf,L"#LRnd%hi:",labelNumber); this->tt->Emit(buf); - labelNumber++; - for (j = 0; j < this->knots; j++) this->attrib[j].vacu = false; // reset - } - } - } - } - if (this->vacuFormLimit >= 0) { - this->tt->Emit(L"#END"); - swprintf(buf,L"#LRnd%hi:",labels); this->tt->Emit(buf); - } - - this->vacuFormLimit = -1; - this->vacuForms = 0; -} /* TTSourceGenerator::DoVacuFormRound */ - -TTGenerator *NewTTSourceGenerator(void) { - return new TTSourceGenerator; -} - -/* Notice that FixDStrokes touches all untouched points of strokes that happen to be diagonal or italic - (but not the DStroke or IStroke), but the only point that might stay untouched upon a stroke is the - "upper left" (i.e. B1 in the stroke's sketch), which typically gets touched anyhow as a result of - SetItalicStrokePhase/Angle, and which is a stationary point (because presumably it has a link to it), - we could simply MDAP[r] it if SetItalicStrokePhase/Angle didn't touch it, and forget about keeping - track of the touched flags... Put another way, most calls to Touched() are redundant, since they mark - a point that was never involved in a non-horizontal and non-vertical STROKE. In any case it looks as - if FixDStrokes, if it remains in use, could be an informative command. Another thing I could do is - to move the Touched() update into the TTEngine, and in order for FixDStrokes to be able to work with - the data hidden, provide for an inquiry function bool Touched(bool y). This could then get us - rid of the FVDir() inquiry function... - - 2007-04-19 Not anymore. In order to implement XSmooth/() we need to know which points have been touched, - and if so, in x or y (or both). Furthermore, the more recent Res* commands encapsulate a plurality of - TT instructions, hence updating a flag inside TTEngine does not make the task of tracking this any easier. - - Question: Does SetItalicStrokePhase/Angle really do what we want it to? Wouldn't we rather want to - move the points down to the base line, then xlink/xinterpolate them (at which point they get rounded), - and finally move them back up, along the (meanwhile) adjusted angle? Notice that this is what SetItalc- - StrokePhase/Angle never did, but chances are, what it was intended to. - - In the future might rewrite also c1_BestClusterIndex and see, whether all of this makes sense the way - it is. - - Plus, should assert that the hinter parameters are up-to-date (compiled or so) prior to code generation. - - Should also assert that eg. VacuFormRound doesn't attempt to 'do' the side-bearing points. This becomes - the longer the more an issue which maybe should be more centralised, such as in the parser. The problem - is that there are things about which only the code generator knows, which means the particular code - generator at issue, such as implementation restrictions (how many links from a point etc.), so the parser - would have to know more about that particular code generator than I like. On the other hand there are - checks that apply to all code generators, and preferreably are done once and forever (ie. in the parser), - such as not to try to VacuFormRound the side bearing points, etc. - - */ +/***** + + TTGenerator.c - New TypeMan Talk Compiler - Code Generators + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +*****/ +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "pch.h" + +//#define maxBitsKnot 12 /* depends on MAXPOINTS, which is (currently) 2048... */ +//#define knotMask 4095L +//#define maxBitsCvt 16 /* cvt numbers seem to be 16bit-integers */ +//#define cvtMask 65535L /* allow for -1 */ +#define strokeFudge STRAIGHTANGLEFUDGE /* = 1.5 degrees */ +#define serifFudge 5 /* degrees */ +#define diagFudge 5 /* degrees */ +#define diagFudgeMT 5 /* degrees */ +#define noJunction 25 /* degrees */ +#define gmndItFudge 12 +#define neighFudge 7.5 /* degrees */ +#define vacuFudge(font) ((this)->emHeight/200) + +#define LSBTMP (FIRSTTMPCVT + 3) +#define RSBTMP (FIRSTTMPCVT + 4) +#define RESERVED_HEIGHTSPACE_START 36 +#define ALIGN_TOLERANCE 0.5 // degrees +#define MAXSTROKESIDEANGLEDIFFERENCE 5.0 /* degrees */ +#define ND_HEIGHT_STORE_1 23 +#define ND_ITALIC_STORE_1 43 +#define MAXIMUM_ITALIC_ANGLE_DEVIATION 3.5 +#define MAIN_STROKE_IS_ONE_PIXEL_STORAGE 8 /* boolean */ + +#define maxVacuForms 16L + +typedef struct { + Rounding round[2]; // xRomanDir, yRomanDir + short cvt; // YAnchor's cvt for IStroke optimization + bool touched[2]; // xRomanDir, yRomanDir, have to keep track of for FixDStrokes (?) + bool dStroke; // part of a Stroke which is neither diagonal nor horizontal + bool iStroke; // part of a Stroke which is sufficiently close to the main stroke angle + bool on; // on-curve point + bool vacu; // temp use in VacuFormRound + short deltaAngle; // (rounded) degrees, left turn => positive, right turn => negative, straight: 0 +} Attribute; +typedef Attribute* AttributePtr; + +typedef struct { + short type; + short radius; + short cvt; + bool forward[2]; + short knot[4]; +} VacuFormParams; + +typedef Vector* VectorPtr; + +const Rounding deltaRounding[roff-rthg+1][roff-rthg+1] = {{rtg, rtdg, rthg, roff, roff, roff}, + {roff, rtdg, roff, roff, roff, roff}, + {rthg, rtdg, rtg, rdtg, rutg, roff}, + {rthg, rtdg, rtg, rdtg, rutg, roff}, + {rthg, rtdg, rtg, rdtg, rutg, roff}, + {roff, roff, roff, roff, roff, roff}}; + +const F26Dot6 defaultMinDistAmount[roff-rthg+1][roff-rthg+1] = {{one6, half6, half6, half6, half6, 0}, + {half6, half6, half6, half6, half6, 0}, + {half6, half6, one6, 0, one6, 0}, + {half6, half6, one6, 0, one6, 0}, + {half6, half6, one6, 0, one6, 0}, + {0, 0, 0, 0, 0, 0}}; + +void TTGenerator::MainStrokeAngle(short angle100, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::SetRounding(bool y, Rounding round, short params, short param[]) { /* abstract */ } +void TTGenerator::SetItalicStroke(bool phase, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::GrabHereInX(short left, short right, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::BeginCodePath(short fpgmBias, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::EndCodePath(wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[], size_t errMsgLen) { /* abstract */ } +void TTGenerator::Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::FixDStrokes(void) { /* abstract */ } +void TTGenerator::Serif(bool forward, short type, short knots, short knot[], wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Scoop(short parent0, short child, short parent1, wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Smooth(short y, short italicFlag) { /* abstract */ } +void TTGenerator::Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[], size_t errMsgLen) { /* abstract */ } +void TTGenerator::VacuFormLimit(short ppem) { /* abstract */ } +void TTGenerator::VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Call(short actParams, short anyNum[], short functNum) { /* abstract */ } +void TTGenerator::Asm(bool inLine, wchar_t text[], wchar_t error[], size_t errorLen) { /* abstract */ } +void TTGenerator::Quit(void) { /* abstract */ } +void TTGenerator::InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError) { /* abstract */ } +void TTGenerator::TermTTGenerator(void) { /* abstract */ } +TTGenerator::TTGenerator(void) { /* abstract */ } +TTGenerator::~TTGenerator(void) { /* abstract */ } + +class DiagParam : public ListElem { +public: + DiagParam(void); + virtual ~DiagParam(void); + bool leftStationary[2]; + short knot[4]; +}; + +class AlignParam : public ListElem { +public: + AlignParam(void); + virtual ~AlignParam(void); + short parent0,parent1,children,child[maxParams]; +}; + +DiagParam::DiagParam(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // DiagParam::DiagParam + +DiagParam::~DiagParam(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // DiagParam::~DiagParam + +AlignParam::AlignParam(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // AlignParam::AlignParam + +AlignParam::~AlignParam(void) { + // nix, but have to have at least a (pair of) method(s) or else the compiler complains... +} // AlignParam::~AlignParam + +typedef enum {fvOnX, fvOnY, fvOnPV, fvOnLine} FVMTDirection; + +class TTSourceGenerator : public TTGenerator { +public: + virtual void MainStrokeAngle(short angle100, wchar_t error[], size_t errorLen); + virtual void GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[], size_t errorLen); + virtual void SetRounding(bool y, Rounding round, short params, short param[]); + virtual void SetItalicStroke(bool phase, wchar_t error[], size_t errorLen); + virtual void Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[], size_t errorLen); + virtual void GrabHereInX(short left, short right, wchar_t error[], size_t errorLen); + virtual void Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[], size_t errorLen); + virtual void Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[], size_t errorLen); + virtual void BeginCodePath(short fpgmBias, wchar_t error[], size_t errorLen); + virtual void EndCodePath(wchar_t error[], size_t errorLen); + virtual void ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[], size_t errorLen); + virtual void ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[], size_t errorLen); + virtual void ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[], size_t errorLen); + virtual void ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[], size_t errorLen); + virtual void ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[], size_t errorLen); + virtual void ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen); + virtual void ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen); + virtual void ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen); + virtual void Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[], size_t errorLen); + virtual void Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[], size_t errorLen); + virtual void Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[], size_t errMsgLen); + virtual void Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[], size_t errorLen); + virtual void Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[], size_t errorLen); + virtual void DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[], size_t errorLen); + virtual void IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[], size_t errorLen); + virtual void FixDStrokes(void); + virtual void Serif(bool forward, short type, short knots, short knot[], wchar_t error[], size_t errorLen); + virtual void Scoop(short parent0, short child, short parent1, wchar_t error[], size_t errorLen); + virtual void Smooth(short y, short italicFlag); + virtual void VacuFormLimit(short ppem); + virtual void VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[], size_t errorLen); + virtual void Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[], size_t errMsgLen); + virtual void Call(short actParams, short anyNum[], short functNum); + virtual void Asm(bool inLine, wchar_t text[], wchar_t error[], size_t errorLen); + virtual void Quit(void); + virtual void InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError); + virtual void InitCodePathState(void); + virtual void TermCodePathState(void); + virtual void TermTTGenerator(void); + TTSourceGenerator(void); + virtual ~TTSourceGenerator(void); +private: + ProjFreeVector xRomanPV,yRomanPV,xItalPV,yItalPV,xAdjItalPV,yAdjItalPV; // generalized projection freedom vectors used as actual parameters in frequent standard cases + RVector xAxis,yAxis,slope; // of italics, normalised + short riseCvt,runCvt; // for GlyphStrokeAngle + double cosF,tanF; // of strokeFudge + double cosF0; // of MAXSTROKESIDEANGLEDIFFERENCE + double cosF1; // of ALIGN_TOLERANCE + double cosF2,sinF22; // of MAXIMUM_ITALIC_ANGLE_DEVIATION, 2*MAXIMUM_ITALIC_ANGLE_DEVIATION + double sinF3; // of serifFudge + double tanF4; // of diagFudge + double cosF5; // of gmndItFudge + double cosF6; // of neighFudge + double cosMT; // of diagFudgeMT + double tanMT; // of diagFudgeMT + bool italic; // an italic font with mainStrokeAngle other than 90 degree ±STRAIGHTANGLEFUDGE + bool mainStrokeAngle,glyphStrokeAngle,setItalicStrokePhase,setItalicStrokeAngle; + bool xSmooth,ySmooth; + short leftAnchor,rightAnchor; // for GrabHereInX + TrueTypeFont *font; + TrueTypeGlyph *glyph; + int32_t glyphIndex,charCode,emHeight; + CharGroup charGroup; // mapped from current glyph + short knots; + AttributePtr attrib; // allocate just as many as we need + VectorPtr V; // here, too + LinearListStruct *diagonals; + LinearListStruct *aligns; + short vacuFormLimit; // ppem size at which vacuforming is turned off + short vacuForms; + VacuFormParams vacuForm[maxVacuForms]; // but not here + bool legacyCompile; + + TTEngine *tt; // if no TTEngine is used, then assume we are building up the cvt + + bool ItalicAngleAllowed(ProjFreeVector *projFreeVector, wchar_t error[], size_t errorLen); + RVector MakeRVector(const TTVectorDesc *ttv, bool pv); + bool AlmostPerpendicular(const TTVectorDesc *pv, const TTVectorDesc *fv, wchar_t error[], size_t errorLen); + void AssertFreeProjVector(const TTVectorDesc *pv, const TTVectorDesc *fv); + short ProjectedDistance(bool signedDistance, short parent, short child, ProjFreeVector *projFreeVector); + void AssertPositiveProjectedDistance(short *parent, short *child, ProjFreeVector *projFreeVector); + void CondRoundInterpolees(bool y, short children, short child[], Rounding actual[], Rounding targeted); // used only once, why a method, then? + void AssertStrokePhaseAngle(FVOverride fv, bool leftStationary[], short knot[]); + short Neighbour(short parent0, short parent1, short child, bool immediate); + void AssertVectorsAtPotentialJunction(TTVector pv, short parent0, short parent1, short child); + FVMTDirection CalcDiagonalFVMT(FVOverride fv, short parent0, short parent1, short child, RVector strokeDirection, short *refPoint0, short *refPoint1); + FVMTDirection CalcAlignFVMT(FVOverride fv, short parent0, short parent1, short child, RVector alignDirection, short *refPoint0, short *refPoint1); + void AssertFVMT(FVMTDirection fvmt, short point0, short point1); + void Touched(short knot, TTVDirection dir); + short TheCvt(short parent, short child, LinkColor color, LinkDirection direction, CvtCategory category, short distance); // color, category, and distance: -1 or invalidCvtNum for default + void DoVacuFormRound(void); +}; + +short Next(short knot, short base, short n, short delta) { + return (knot - base + delta)%n + base; +} // Next + +bool SameVectorsForAllChildren(ProjFreeVector *projFreeVector, int32_t children) { + int32_t i; + TTVectorDesc fv; + + if (children <= 1) return true; + + // we only have to look at the freedom vectors, since there is only one projection vector to begin with + fv = projFreeVector->fv[0]; + for (i = 1; i < children && projFreeVector->fv[i].dir == fv.dir && projFreeVector->fv[i].from == fv.from && projFreeVector->fv[i].to == fv.to; i++); + + return i == children; +} // SameVectorsForAllChildren + +void TTSourceGenerator::MainStrokeAngle(short angle100, wchar_t error[], size_t errorLen) { + double deg = (double)angle100/100.0,rad = Rad(deg); + + this->slope.x = 0.0; + this->slope.y = 1.0; + this->italic = false; + + if (this->mainStrokeAngle) { + swprintf(error,errorLen,L"cannot use MAINSTROKEANGLE more than once per glyph, or together with GLYPHSTROKEANGLE"); + } else { + this->mainStrokeAngle = this->glyphStrokeAngle = true; + if (deg < 90.0 - strokeFudge || 90.0 + strokeFudge < deg) { + this->slope.x = cos(rad); + this->slope.y = sin(rad); + this->italic = true; + } + } +} /* TTSourceGenerator::MainStrokeAngle */ + +void TTSourceGenerator::GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[], size_t errorLen) { + short riseCvtValue,runCvtValue; + short deg; + double rad; + + if (this->glyphStrokeAngle) + swprintf(error,errorLen,L"cannot use GLYPHSTROKEANGLE more than once per glyph, or together with MAINSTROKEANGLE"); + else { + this->font->TheCvt()->GetCvtValue(riseCvt,&riseCvtValue); + this->font->TheCvt()->GetCvtValue(runCvt, &runCvtValue); + if (!riseCvtValue) { swprintf(error,errorLen,L"Cvt value of italic rise %hi cannot be 0",riseCvt); return; } + if (!runCvtValue) { swprintf(error,errorLen,L"Cvt value of italic run %hi cannot be 0",runCvt); return; } + this->riseCvt = riseCvt; + this->runCvt = runCvt; + rad = atan((double)riseCvtValue/(double)runCvtValue); deg = (short)Round(100*Deg(rad)); + this->MainStrokeAngle(deg,error, errorLen); + this->tt->CALL88(riseCvt,runCvt); + } +} // TTSourceGenerator::GlyphStrokeAngle + +RVector TTSourceGenerator::MakeRVector(const TTVectorDesc *ttv, bool pv) { + RVector v; + double len; + + switch (ttv->dir) { + case xRomanDir: + v.x = 1; v.y = 0; + break; + case yRomanDir: + v.x = 0; v.y = 1; + break; + case xItalDir: + case xAdjItalDir: + if (pv) { + v.x = this->slope.y; v.y = -this->slope.x; // not yet distinguishing adjItalDir from italDir + } else { + v.x = 1; v.y = 0; + } + break; + case yItalDir: + case yAdjItalDir: + if (pv) { + v.x = 0; v.y = 1; + } else { + v.x = this->slope.x; v.y = this->slope.y; // not yet distinguishing adjItalDir from italDir + } + break; + case diagDir: + v.x = this->glyph->x[ttv->to] - this->glyph->x[ttv->from]; + v.y = this->glyph->y[ttv->to] - this->glyph->y[ttv->from]; + len = LengthR(v); + v.x /= len; + v.y /= len; + break; + case perpDiagDir: + v.x = this->glyph->y[ttv->to] - this->glyph->y[ttv->from]; + v.y = this->glyph->x[ttv->from] - this->glyph->x[ttv->to]; + len = LengthR(v); + v.x /= len; + v.y /= len; + break; + } + return v; +} // TTSourceGenerator::MakeRVector + +bool TTSourceGenerator::ItalicAngleAllowed(ProjFreeVector *projFreeVector, wchar_t error[], size_t errorLen) { + bool italic = xItalDir <= projFreeVector->pv.dir && projFreeVector->pv.dir <= yAdjItalDir; + + if (italic && !this->italic) { + swprintf(error,errorLen,L"cannot use / (italic angle) or // (adjusted italic angle) unless GLYPHSTROKEANGLE specifies an italic glyph"); + return false; + } + return true; +} // TTSourceGenerator::ItalicAngleAllowed + +bool TTSourceGenerator::AlmostPerpendicular(const TTVectorDesc *pv, const TTVectorDesc *fv, wchar_t error[], size_t errorLen) { + RVector rpv,rfv; + + rpv = this->MakeRVector(pv,true); + rfv = this->MakeRVector(fv,false); + + if (Abs(ScalProdRV(rpv,rfv)) >= 1.0/16.0) return false; + + swprintf(error,errorLen,L"cannot accept vector override (projection and freedom vectors are [almost] perpendicular)"); + return true; +} // TTSourceGenerator::AlmostPerpendicular + +void TTSourceGenerator::SetRounding(bool y, Rounding round, short params, short param[]) { + short i; + + for (i = 0; i < params; i++) this->attrib[param[i]].round[y] = round; +} /* TTSourceGenerator::SetRounding */ + +void TTSourceGenerator::SetItalicStroke(bool phase, wchar_t error[], size_t errorLen) { + if (phase) { + if (this->setItalicStrokePhase) + swprintf(error,errorLen,L"cannot use SETITALICSTROKEPHASE more than once per glyph"); + else + this->setItalicStrokePhase = true; + } else { + if (this->setItalicStrokeAngle) + swprintf(error, errorLen, L"cannot use SETITALICSTROKEANGLE more than once per glyph"); + else + this->setItalicStrokeAngle = true; + } +} /* TTSourceGenerator::SetItalicStroke */ + +void TTSourceGenerator::Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[], size_t errorLen) { + Rounding knotR; + bool negativeDist; + + knotR = this->attrib[knot].round[y]; + if (knot < this->knots - PHANTOMPOINTS || (!y && (knotR == rdtg || knotR == rutg))) { + if (!this->ItalicAngleAllowed(projFreeVector,error, errorLen)) return; + if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error, errorLen)) return; + if (this->tt) { + this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); + round = round && knotR != roff; // there is a rounding method to be asserted... + // same roundabout way as in Link to maintain philosophy that knot gets rounded, not (absolute or relative) distance + negativeDist = y ? this->V[knot].y < 0 : this->V[knot].x < 0; + if (negativeDist && rdtg <= knotR && knotR <= rutg) knotR = (Rounding)(((short)knotR - (short)rdtg + 1)%2 + (short)rdtg); // rdtg <=> rutg + if (round) this->tt->AssertRounding(knotR); + if (y) this->attrib[knot].cvt = cvt; // make it available to IStroke... + if (cvt < 0) this->tt->MDAP(round,knot); else this->tt->MIAP(round,knot,cvt); // if (still) no cvt... + this->Touched(knot,projFreeVector->fv[0].dir); + } else { + /* nix */ + } + } else if (y) { + swprintf(error,errorLen,L"cannot YANCHOR the side-bearing points"); + } else { + swprintf(error,errorLen,L"can XANCHOR the side-bearing points only to grid, down to grid, or up to grid"); + } +} /* TTSourceGenerator::Anchor */ + +void TTSourceGenerator::GrabHereInX(short left, short right, wchar_t error[], size_t errorLen) { + short leftDistance,leftCvt,rightDistance,rightCvt; + double dist; + Vector link; + + if (left < this->knots - PHANTOMPOINTS && right < this->knots - PHANTOMPOINTS) { + link = SubV(this->V[left],this->V[this->knots - 2]); + dist = link.x*this->slope.y - link.y*this->slope.x; // signed projection onto the normal to the direction of the main stroke angle + leftDistance = (short)Round(dist); + link = SubV(this->V[this->knots - 1],this->V[right]); + dist = link.x*this->slope.y - link.y*this->slope.x; // signed projection onto the normal to the direction of the main stroke angle + rightDistance = (short)Round(dist); + if (this->tt) { + leftCvt = this->TheCvt(-1,-1,linkGrey,linkX,cvtLsb,leftDistance); + rightCvt = this->TheCvt(-1,-1,linkWhite,linkX,cvtRsb,rightDistance); + if (leftCvt < 0 ) + swprintf(error,errorLen,L"cannot accept GRABHEREINX (no cvt found from %hi to %hi)",this->knots - 2,left); + else if (rightCvt < 0) + swprintf(error,errorLen,L"cannot accept GRABHEREINX (no cvt found from %hi to %hi)",right,this->knots - 1); + else { + this->leftAnchor = left; this->rightAnchor = right; + this->tt->CALL24(leftCvt,rightCvt); + } + } + } else + swprintf(error,errorLen,L"cannot accept GRABHEREINX (%hi is a side-bearing point)",left >= this->knots - PHANTOMPOINTS ? left : right); +} /* TTSourceGenerator::GrabHereInX */ + +short TTSourceGenerator::ProjectedDistance(bool signedDistance, short parent, short child, ProjFreeVector *projFreeVector) { + int32_t distance; + Vector link; + RVector pv; + double temp; + + if (projFreeVector->pv.dir == xRomanDir) { + distance = this->V[child].x - this->V[parent].x; + } else if (projFreeVector->pv.dir == yRomanDir || projFreeVector->pv.dir == yItalDir || projFreeVector->pv.dir == yAdjItalDir) { + // yItalDir and yAdjItalDir have their pv in Y (but their fv in italic direction) + distance = this->V[child].y - this->V[parent].y; + } else { + // general case: need to actually project + if (projFreeVector->pv.dir == xItalDir || projFreeVector->pv.dir == xAdjItalDir) { + // xItalDir and xAdjItalDir have their pv perp italic direction (but their fv in X) + pv.x = this->slope.y; + pv.y = -this->slope.x; + } else { + pv = RDirectionV(this->V[projFreeVector->pv.from],this->V[projFreeVector->pv.to]); + if (projFreeVector->pv.dir == perpDiagDir) { temp = pv.x; pv.x = pv.y; pv.y = -temp; } + } + link = SubV(this->V[child],this->V[parent]); + distance = Round(link.x*pv.x + link.y*pv.y); + } + return (short)(signedDistance ? distance : Abs(distance)); +} // TTSourceGenerator::ProjectedDistance + +void TTSourceGenerator::AssertPositiveProjectedDistance(short *parent, short *child, ProjFreeVector *projFreeVector) { + short temp; + + if (this->ProjectedDistance(true,*parent,*child,projFreeVector) < 0) { + temp = *parent; *parent = *child; *child = temp; + } +} // TTSourceGenerator::AssertPositiveProjectedDistance + +void TTSourceGenerator::Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[], size_t errorLen) { + TTVDirection dir; + Vector link; + RVector linkDirection; + double vectProd,scalProd; + short distance = 0,parentC,childC; + LinkColor color; + Rounding deltaR = rthg,parentR,childR; + bool italicLink,lsbLink,rsbLink,negativeDist,negativeMirp; + + linkDirection.x = 0; + linkDirection.y = 0; + + lsbLink = rsbLink = false; + if (!this->ItalicAngleAllowed(projFreeVector,error, errorLen)) return; + if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error, errorLen)) return; + dir = projFreeVector->pv.dir; + italicLink = this->italic && (dir == xItalDir || dir == xAdjItalDir); + negativeDist = negativeMirp = false; + if (y) { + color = this->glyph->TheColor(parent,child); + link = SubV(this->V[child],this->V[parent]); linkDirection = RDirectionV(this->V[child],this->V[parent]); + negativeDist = link.y < 0; + if (!this->legacyCompile) + { + distance = this->ProjectedDistance(false, parent, child, projFreeVector); + } + else + { + distance = (short)Abs(link.y); + } + } else { // check for links related to GrabHereInX, using predefined colors and cvt numbers + if ((parent == this->leftAnchor && child == this->knots - 2) || (parent == this->knots - 2 && child == this->leftAnchor)) { + lsbLink = true; negativeMirp = parent == this->leftAnchor; + if (dist) swprintf(error,errorLen,L"cannot use an XDIST command when a GRABHEREINX command has defined a cvt number"); + if (cvt >= 0) swprintf(error,errorLen,L"cannot override a cvt number defined via a GRABHEREINX command"); + color = linkGrey; cvt = LSBTMP; + } else if ((parent == this->rightAnchor && child == this->knots - 1) || (parent == this->knots - 1 && child == this->rightAnchor)) { + rsbLink = true; negativeMirp = parent == this->rightAnchor; + if (dist) swprintf(error,errorLen,L"cannot use an XDIST command when a GRABHEREINX command has defined a cvt number"); + if (cvt >= 0) swprintf(error,errorLen,L"cannot override a cvt number defined via a GRABHEREINX command"); + color = linkWhite; cvt = RSBTMP; + } else { + color = this->glyph->TheColor(parent,child); + link = SubV(this->V[child],this->V[parent]); linkDirection = RDirectionV(this->V[child],this->V[parent]); + if (italicLink) { + vectProd = link.x*this->slope.y - link.y*this->slope.x; + negativeDist = vectProd < 0; distance = (short)Round(Abs(vectProd)); // unsigned distance... project onto the normal to the direction of the main stroke angle + } else { + negativeDist = link.x < 0; + if (!this->legacyCompile) + { + distance = this->ProjectedDistance(false, parent, child, projFreeVector); + } + else + { + distance = (short)Abs(link.x); + } + } + } + } + parentR = this->attrib[parent].round[y]; + childR = this->attrib[child].round[y]; + + if (this->tt) this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); + + scalProd = linkDirection.x*this->slope.x + linkDirection.y*this->slope.y; + if (parentR == childR && !lsbLink && !rsbLink && (distance == 0 || (italicLink && scalProd > this->cosF1))) { // angle between link and slope < 0.5° + if (this->tt) { + this->tt->AssertRefPoint(0,parent); + // ALIGNRP is less optimal than (but equivalent to) an unrounded MDRP, since for chains of Dists, + // we keep setting the reference point explicitely, while MDRP can simply move it along + if (distance == 0) { + this->tt->AssertRounding(rtg); + this->tt->MDRP(false,true,linkGrey,child); // almost same as in ::Align + } else + this->tt->ALIGNRP(child); + } + } else { + if (this->tt) { + if (!this->legacyCompile) + { + // we simply round the distance according to the child point's rounding method; see also further comments below + deltaR = postRoundFlag ? roff : childR; + // if (parentR == rthg && childR == rthg) deltaR = rtg; + // else if (parentR == rthg && childR == rtg) deltaR = rthg; + } + else + { + deltaR = postRoundFlag ? roff : deltaRounding[parentR][childR]; + } + if (negativeDist && rdtg <= deltaR && deltaR <= rutg) deltaR = (Rounding)(((short)deltaR - (short)rdtg + 1)%2 + (short)rdtg); // rdtg <=> rutg + if (minDists < 0) { // no minDist override => get viable defaults + minDists = 0; + if (color == linkBlack) { + for (parentC = 0; (parentC < this->glyph->numContoursInGlyph && this->glyph->endPoint[parentC] < parent); parentC++); + for (childC = 0; (childC < this->glyph->numContoursInGlyph && this->glyph->endPoint[childC] < child); childC++); + if ((distance >= this->emHeight/100 && parentC == childC) || (distance >= this->emHeight/50 && parentC != childC)) { + // this uses just the same assumptions as did the old compiler to guess whether or not it makes sense to assert a minimum distance + jumpPpemSize[0] = 1; + if (!this->legacyCompile) + { + pixelSize[0] = defaultMinDistAmount[childR][childR]; + } + else + { + pixelSize[0] = defaultMinDistAmount[parentR][childR]; + } + if (negativeDist && rdtg <= deltaR && deltaR <= rutg) pixelSize[0] = (pixelSize[0] + one6)%two6; // one6 <=> 0 + if (pixelSize[0] > 0) minDists++; + } + } + } + this->tt->AssertMinDist(minDists,jumpPpemSize,pixelSize); + if (!postRoundFlag && deltaR != roff) this->tt->AssertRounding(deltaR); + this->tt->AssertRefPoint(0,parent); + } + if (italicLink && !lsbLink && !rsbLink && scalProd > this->cosF2) { // angle between link and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION + if (this->tt) this->tt->MDRP(minDists > 0,deltaR != roff,color,child); + } else if (this->tt) { + if (!dist && cvt < 0) { // Link command and (still) no cvt => get default from cvt table + cvt = this->TheCvt(parent,child,color,y ? linkY : linkX,category,distance); + } + if (dist || cvt < 0) { // Dist command or Link command and (still) no cvt (depends on how cvt "comments" were setup) + this->tt->MDRP(minDists > 0,deltaR != roff,color,child); + } else { + this->tt->AssertAutoFlip(!lsbLink && !rsbLink); // autoflip affects only MIRP + this->tt->MIRP(minDists > 0,deltaR != roff,color,child,cvt,negativeMirp); + } + } // else cvt entered in GrabHereInX + if (this->tt && !postRoundFlag && deltaR == roff && childR != roff) { // can't do otherwise + // I think the original idea here was to ensure that the rounding method is an attribute of the knot, + // i.e. if I wanted the knot "down-to-grid" then it will end up "down-to-grid", and not the distance + // inbetween. As long as our fv and pv are X or Y, this is achieved by either adjusting the rounding + // method of the distance accordingly (deltaRounding table), or else don't round the distance and do + // round the knot afterwards. As soon as we're using a pv other than X or Y, and especially when we're + // using the dpv, the whole idea makes little sense, since the respective knots are no longer parallel + // to any grid. Furthermore, when using the dpv, rounding afterwards would be pretty useless anyway, + // because MDAP never uses the dpv in the first place. Therefore, and currently #ifdef'd for VTT_PRO_SP_YAA_AUTO_COM + // only, the distance is rounded as per the rounding method of the child point. + this->tt->AssertRounding(childR); + this->tt->MDAP(true,child); + } + } + if (this->tt && postRoundFlag && childR != roff) { + this->tt->AssertFreeProjVector(xRomanDir); // only allowed on XDist, XLink + this->tt->AssertRounding(childR); + this->tt->MDAP(true,child); + } + if (this->tt) this->Touched(child,projFreeVector->fv[0].dir); + *actualCvt = lsbLink || rsbLink ? invalidCvtNum : cvt; +} /* TTSourceGenerator::Link */ + +/***** have to test this on the visual level, because we might end up with the following scenario: +- yinterpolate two children with exactly the same y-coordinate (e.g. points 1 and 47 in Times New Roman 'M'). +- round first of them to grid, translating to a YIPAnchor and a YInterpolate +- YInterpolate has rounded interpolee as parent of unrounded interpolee (Ian's and Vinnie's wish) +- but now unrounded interpolee is at same y-coordinate as its (temporary) parent +bool ValidateInterpolee(bool y, short parent0, short child, short parent1, Vector V[], wchar_t error[], size_t errorLen); +bool ValidateInterpolee(bool y, short parent0, short child, short parent1, Vector V[], wchar_t error[], size_t errorLen) { + int32_t low,mid,high; + short parent; + wchar_t dir; + + if (y) { + low = Min(V[parent0].y,V[parent1].y); + high = Max(V[parent0].y,V[parent1].y); + mid = V[child].y; + } else { + low = Min(V[parent0].x,V[parent1].x); + high = Max(V[parent0].x,V[parent1].x); + mid = V[child].x; + } + if (low < mid && mid < high) return true; // accept + dir = 'x' + (y & true); + if (low == mid || mid == high) { + parent = y ? (mid == V[parent0].y ? parent0 : parent1) : (mid == V[parent0].x ? parent0 : parent1); + swprintf(error,L"cannot %cInterpolate child point %hi (cannot be on same %c-coordinate as its parent point %hi)",Cap(dir),child,dir,parent); + } else { + swprintf(error,L"cannot %cInterpolate child point %hi (cannot be outside the range of %c-coordinates defined by its parent points %hi and %hi)",Cap(dir),child,dir,parent0,parent1); + } + return false; +} // ValidateInterpolee +*****/ + +void TTSourceGenerator::Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[], size_t errorLen) { + short i,r; + Rounding rounding[maxParams]; + + if (!this->ItalicAngleAllowed(projFreeVector,error, errorLen)) return; + if (this->tt) { + this->tt->AssertRefPointPair(1,2,parent0,parent1); + + if (SameVectorsForAllChildren(projFreeVector,children)) { + if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error, errorLen)) return; + this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); + if (children <= 2) { // optimise for 2 or less interpolations + for (i = 0; i < children; i++) this->tt->IP(1,&child[i]); + } else { + this->tt->SLOOP(children); + this->tt->IP(children,child); + } + for (i = 0; i < children; i++) this->Touched(child[i],projFreeVector->fv[i].dir); + } else { + for (i = 0; i < children; i++) { + if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error, errorLen)) return; + this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[i]);; + this->tt->IP(1,&child[i]); + this->Touched(child[i],projFreeVector->fv[i].dir); + } + } + if (round || postRoundFlag) { + if (postRoundFlag) this->tt->AssertFreeProjVector(xRomanDir); // only allowed on XInterpolate, XIPAnchor + for (i = 0; i < children; i++) rounding[i] = this->attrib[child[i]].round[y]; + for (r = (short)rthg; r <= (short)rutg; r++) this->CondRoundInterpolees(y,children,child,rounding,(Rounding)r); + } + // else, strictly speaking, we should set this->attrib[child[i]].round[y] to roff (the default is rtg, which gets modified by Interpolate to roff) + } +} /* TTSourceGenerator::Interpolate */ + +void TTSourceGenerator::BeginCodePath(short fpgmBias, wchar_t error[], size_t errorLen) { + this->tt->SetFunctionNumberBias(fpgmBias); + this->InitCodePathState(); +} // TTSourceGenerator::BeginCodePath + +void TTSourceGenerator::EndCodePath(wchar_t error[], size_t errorLen) { + this->TermCodePathState(); +} // TTSourceGenerator::EndCodePath + +// Notice that for the implementation of the new Rendering Environment Specific VTT Talk commands we tend to set the reference points from within the respective functions hence there is no need to assert them here. +// The main reason for doing so is the absence of "get-reference-point" instructions: the functions implementing these commands tend to need the parents' (refernece points') original and/or current coordinates. +void TTSourceGenerator::ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[], size_t errorLen) { + if (this->tt == NULL) return; + if (child >= this->knots - PHANTOMPOINTS) { swprintf(error,errorLen,L"cannot Res%cAnchor the side-bearing points",y ? L'Y' : L'X'); return; } + if (!this->ItalicAngleAllowed(projFreeVector,error,errorLen)) return; + if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error, errorLen)) return; + this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); + this->tt->ResMIAP(child,cvt); + this->Touched(child,projFreeVector->fv[0].dir); +} // TTSourceGenerator::ResAnchor + +void TTSourceGenerator::ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[], size_t errorLen) { + if (this->tt == NULL) return; + if (!this->ItalicAngleAllowed(projFreeVector,error,errorLen)) return; + if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error,errorLen)) return; + // limited pv/fv implemented so far only, asserted within fn + this->tt->ResIPMDAP(projFreeVector->pv.dir,postRoundFlag,parent0,child,parent1); + this->Touched(child,projFreeVector->fv[0].dir); +} // TTSourceGenerator::ResIPAnchor + +void TTSourceGenerator::ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[], size_t errorLen) { + bool useMinDist; + + if (this->tt == NULL) return; +// if (parent >= this->knots - PHANTOMPOINTS && child >= this->knots - PHANTOMPOINTS) { swprintf(error,L"cannot Res%cLink the advance width",y ? 'Y' : 'X'); return; } + if (!this->ItalicAngleAllowed(projFreeVector,error,errorLen)) return; + if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[0],error,errorLen)) return; + + useMinDist = minDists > 0 || (minDists < 0 && this->glyph->TheColor(parent,child) == linkBlack); + + this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); + this->tt->ResMIRP(parent,child,cvt,useMinDist); + this->Touched(child,projFreeVector->fv[0].dir); +} // TTSourceGenerator::ResLink + +void TTSourceGenerator::ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[], size_t errorLen) { + if (this->tt == NULL) return; + if (!this->ItalicAngleAllowed(projFreeVector,error, errorLen)) return; + for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error, errorLen)) return; + // ResIPMIRP doesn't like negative distances and complementary phases + this->AssertPositiveProjectedDistance(&parent,&child,projFreeVector); + this->AssertPositiveProjectedDistance(&grandParent0,&grandParent1,projFreeVector); + // limited pv/fv implemented so far only, asserted within fn + this->tt->ResIPMIRP(projFreeVector->pv.dir,strokeOptimizationFlag,grandParent0,parent,child,cvt,grandParent1); + this->Touched(parent,projFreeVector->fv[0].dir); + this->Touched(child, projFreeVector->fv[1].dir); +} // TTSourceGenerator::ResIPLink + +void TTSourceGenerator::ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[], size_t errorLen) { + TTVectorDesc pv[2]; + + if (this->tt == NULL) return; + if (!this->ItalicAngleAllowed(projFreeVector,error, errorLen)) return; + pv[0].dir = perpDiagDir; pv[0].from = parent0; pv[0].to = child1; // criss-crossed... + pv[1].dir = perpDiagDir; pv[1].from = parent1; pv[1].to = child0; // ...diagonal links + for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&pv[i],&projFreeVector->fv[i],error, errorLen)) return; + // pv will be dealt with in fn (that's the very raison d'être of DLink...) + this->tt->ResDDMIRP(parent0,child0,projFreeVector->fv[0],cvt0,parent1,child1,projFreeVector->fv[1],cvt1); + this->Touched(child0,projFreeVector->fv[0].dir); + this->Touched(child1,projFreeVector->fv[1].dir); +} // TTSourceGenerator::ResDDLink + +// it looks like the following three methods are quite similar, but they have rather different semantics: +// ResIPDLink constrains a pair of strokes in x or y by a single constraint each, while ResIPDDLink/ResIIPDLink constrain a diagonal/an italic stroke by a pair of constraints +void TTSourceGenerator::ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen) { + TTVDirection dir = y ? yRomanDir : xRomanDir; + + // strokeOptimizationFlag not yet supported + if (this->tt == NULL) return; +// if (!this->ItalicAngleAllowed(projFreeVector,error)) return; +// for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error)) return; + // ResIPDMIRP doesn't like negative distances and complementary phases + this->AssertPositiveProjectedDistance(&parent0,&child0,projFreeVector); + this->AssertPositiveProjectedDistance(&parent1,&child1,projFreeVector); + this->AssertPositiveProjectedDistance(&grandParent0,&grandParent1,projFreeVector); + // pv/fv limited to x or y by syntax so far + this->tt->ResIPDMIRP(projFreeVector->pv.dir,grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1); + this->Touched(parent0,dir); + this->Touched(child0, dir); + this->Touched(child1, dir); + this->Touched(parent1,dir); +} // TTSourceGenerator::ResIPDLink + +void TTSourceGenerator::ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen) { + TTVDirection dir = y ? yRomanDir : xRomanDir; + + // strokeOptimizationFlag not yet supported + if (this->tt == NULL) return; +// if (!this->ItalicAngleAllowed(projFreeVector,error)) return; +// for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error)) return; + /***** + // ResIPDDMIRP doesn't like negative distances and complementary phases + this->AssertPositiveProjectedDistance(&parent0,&child0,projFreeVector); + this->AssertPositiveProjectedDistance(&parent1,&child1,projFreeVector); + *****/ + this->AssertPositiveProjectedDistance(&grandParent0,&grandParent1,projFreeVector); + this->tt->ResIPDDMIRP(projFreeVector->pv.dir,grandParent0,parent0,child0,projFreeVector->fv[0],cvt0,parent1,child1,projFreeVector->fv[1],cvt1,grandParent1); + this->Touched(parent0,dir); + this->Touched(child0, projFreeVector->fv[0].dir); + this->Touched(child1, projFreeVector->fv[1].dir); + this->Touched(parent1,dir); +} // TTSourceGenerator::ResIPDDLink + +void TTSourceGenerator::ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen) { + if (this->tt == NULL) return; + if (!this->ItalicAngleAllowed(projFreeVector,error, errorLen)) return; + for (short i = 0; i < 2; i++) if (this->AlmostPerpendicular(&projFreeVector->pv,&projFreeVector->fv[i],error,errorLen)) return; + // pv xItalDir, asserted within fn, hence fv = xRomanDir + this->tt->ResIIPDMIRP(grandParent0,parent0,child0,cvt0,parent1,child1,cvt1,grandParent1); + this->Touched(parent0,xRomanDir); + this->Touched(child0, xRomanDir); + this->Touched(child1, xRomanDir); + this->Touched(parent1,xRomanDir); +} // TTSourceGenerator::ResIIPDLink + +void TTSourceGenerator::Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[], size_t errorLen) { + if (this->tt) { + if (ppem0 == noPpemLimit && ppem1 == noPpemLimit) { + this->tt->ISECT(intersection,line0start,line0end,line1start,line1end); + } else if (ppem1 == noPpemLimit) { + this->tt->IfPpemBelow(ppem0); + this->tt->ISECT(intersection,line0start,line0end,line1start,line1end); + this->tt->Else(); + this->Align(fvStandard,line0start,1,&intersection,line0end,ppem0,error, errorLen); + this->Align(fvStandard,line1start,1,&intersection,line1end,noPpemLimit,error, errorLen); + this->tt->End(true); + } else { // both directions have ppem limits + this->tt->IfPpemBelow(Min(ppem0,ppem1)); + this->tt->ISECT(intersection,line0start,line0end,line1start,line1end); + this->tt->Else(); + this->Align(fvStandard,line0start,1,&intersection,line0end,ppem0,error, errorLen); + this->Align(fvStandard,line1start,1,&intersection,line1end,ppem1,error, errorLen); + this->tt->End(true); + } + this->Touched(intersection,diagDir); + } +} // TTSourceGenerator::Intersect + +bool ClassifyAlign(Vector parent0, Vector child, Vector parent1, short ppem) { // within rectangular hull of ±0.5° from "parent line"? + double tanAlignTolerance; + Vector p,q; + int32_t pq,p2; + + if (ppem > 0) return true; // ppem limit specified, hence we'll gladly align anything + + tanAlignTolerance = tan(Rad(ALIGN_TOLERANCE)); + p = SubV(parent1,parent0); + q = SubV(child,parent0); + p2 = p.x*p.x + p.y*p.y; + pq = ScalProdV(p,q); + return 0 <= pq && pq <= p2 && Abs(VectProdV(p,q)) <= p2*tanAlignTolerance; +} // ClassifyAlign + +void TTSourceGenerator::Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[], size_t errorLen) { + short i,ch,iChildren[2],iChild[2][maxParams],refPoint[maxParams][2]; + int32_t minX,minY,x,y,maxX,maxY; + AlignParam *align; + wchar_t buf[8*maxParams]; + RVector alignDirection = RDirectionV(this->V[parent0],this->V[parent1]); + FVMTDirection fvmt[maxParams]; + + if (this->tt) { + switch (fvOverride) { + case fvOldMethod: // Align + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* Align [%hi...%hi] */",parent0,parent1); this->tt->Emit(buf); + + this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); + for (i = 0; i < children; i++) { + ch = child[i]; + this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); + if (ClassifyAlign(this->V[parent0],this->V[ch],this->V[parent1],ppem)) + this->tt->ALIGNRP(ch); + else { + this->tt->AssertRounding(rdtg); + this->tt->MDRP(false,true,linkGrey,ch); + } + this->Touched(ch,this->tt->FVDir()); + } + break; + case fvForceX: // XAlign + case fvForceY: // YAlign + case fvStandard: // DAlign + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* %cAlign [%hi...%hi] */",fvOverride == fvStandard ? L'D' : (fvOverride == fvForceX ? L'X' : L'Y'),parent0,parent1); this->tt->Emit(buf); + + minX = Min(this->V[parent0].x,this->V[parent1].x); maxX = Max(this->V[parent0].x,this->V[parent1].x); + minY = Min(this->V[parent0].y,this->V[parent1].y); maxY = Max(this->V[parent0].y,this->V[parent1].y); + + for (i = iChildren[0] = iChildren[1] = 0; i < children; i++) { + ch = child[i]; + + fvmt[i] = CalcAlignFVMT(fvOverride,parent0,parent1,ch,alignDirection,&refPoint[i][0],&refPoint[i][1]); + // if (fvOverride != fvStandard) { + // always touch point in both directions or else aligning is undermined + x = this->V[ch].x; + y = this->V[ch].y; + if (fvmt[i] == fvOnX && !this->attrib[ch].touched[yRomanDir] && !this->ySmooth && minY < y && y < maxY) iChild[true][iChildren[true]++] = ch; + if (fvmt[i] == fvOnY && !this->attrib[ch].touched[xRomanDir] && !this->xSmooth && minX < x && x < maxX) iChild[false][iChildren[false]++] = ch; + // } + + } + if (iChildren[true] > 0) this->Interpolate(true,&this->yRomanPV,false,parent0,iChildren[true],iChild[true],parent1,false,error,errorLen); + if (iChildren[false] > 0) this->Interpolate(false,&this->xRomanPV,false,parent0,iChildren[false],iChild[false],parent1,false,error,errorLen); + + this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); + // /****** if (ppem >= 0) *****/ this->tt->AssertRoundingBelowPpem(rdtg,ppem); + if (ppem != 1) this->tt->RoundDownToGridBelowPpem(ppem); // Jelle special optimisation + this->tt->AssertTTVonLine(dpv,parent0,parent1,this->V[parent0],this->V[parent1],true); + for (i = 0; i < children; i++) { + ch = child[i]; + this->AssertFVMT(fvmt[i],refPoint[i][0],refPoint[i][1]); + this->tt->MDRP(false,ppem != 1/***** true *****//***** ppem >= 0 *****/,linkGrey,ch); + this->Touched(ch,this->tt->FVDir()); + } + + break; + + // swprintf(buf,L"/* DAlign [%hi...%hi] */",parent0,parent1); this->tt->Emit(buf); + + // this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); + // if (ppem >= 0) this->tt->AssertRoundingBelowPpem(rdtg,ppem); + // for (i = 0; i < children; i++) { + // ch = child[i]; + // this->AssertVectorsAtPotentialJunction(dpv,parent0,parent1,ch); + // this->tt->MDRP(false,ppem >= 0,linkGrey,ch); + // this->Touched(ch,this->tt->FVDir()); + // } + // break; + default: + break; + } + + // ***** an experimental variant ***** + // alignDirection = RDirectionV(this->V[parent0],this->V[parent1]); + // this->tt->IfPpemBelow(ppem); + // tt->AssertTTVonLine(pv,parent0,parent1,this->V[parent0],this->V[parent1],true); + // for (i = 0; i < children; i++) { + // ch = child[i]; + // this->AssertFVMT(parent0,parent1,ch,alignDirection); + // this->tt->ALIGNRP(ch); + // this->Touched(ch,this->tt->FVDir()); + // } + // this->tt->Else(); + // tt->AssertTTVonLine(dpv,parent0,parent1,this->V[parent0],this->V[parent1],true); + // for (i = 0; i < children; i++) { + // ch = child[i]; + // this->AssertFVMT(parent0,parent1,ch,alignDirection); + // this->tt->MDRP(false,false,linkGrey,ch); + // this->Touched(ch,this->tt->FVDir()); + // } + // this->tt->End(); + // ***** another experimental variant + // this->tt->IfPpemBelow(ppem); + // for (i = 0; i < children; i++) { + // ch = child[i]; + // this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); + // this->tt->ALIGNRP(ch); + // this->Touched(ch,this->tt->FVDir()); + // } + // this->tt->Else(); + // for (i = 0; i < children; i++) { + // ch = child[i]; + // this->AssertVectorsAtPotentialJunction(dpv,parent0,parent1,ch); + // this->tt->MDRP(false,false,linkGrey,ch); + // this->Touched(ch,this->tt->FVDir()); + // } + // this->tt->End(); + // ***** + + align = new AlignParam; + align->parent0 = parent0; align->parent1 = parent1; + align->children = children; + for (i = 0; i < children; i++) align->child[i] = child[i]; + this->aligns->InsertAtEnd(align); + } + +// if (this->tt) { +// this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); +// if (ppem < 0) { +// for (i = 0; i < children; i++) { +// ch = child[i]; +// this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); +// if (ClassifyAlign(this->V[parent0],this->V[ch],this->V[parent1],ppem)) +// this->tt->ALIGNRP(ch); +// else +// this->tt->MDRP(false,false,linkGrey,ch); +// this->Touched(ch,this->tt->FVDir()); +// } +// } else { +// this->tt->AssertRoundingBelowPpem(rdtg,ppem); +// for (i = 0; i < children; i++) { +// ch = child[i]; +// this->AssertVectorsAtPotentialJunction(dpv,parent0,parent1,ch); +// this->tt->MDRP(false,true,linkGrey,ch); +// this->Touched(ch,this->tt->FVDir()); +// } +// } +// } +} // TTSourceGenerator::Align + +/***** +void TTSourceGenerator::Align(short parent0, short children, short child[], short parent1, short ppem, wchar_t error[], size_t errorLen) { + short i,ch; + RVector parentDir,childDir; + + if (this->tt) { + parentDir = RDirectionV(this->V[parent1],this->V[parent0]); + for (i = 0; i < children; i++) { + ch = child[i]; + this->AssertVectorsAtPotentialJunction(pv,parent0,parent1,ch); + this->tt->AssertEitherKnotOnRefPoint(parent0,parent1,0); + childDir = RDirectionV(this->V[ch],this->V[parent0]); + // distance = childV.x*slope.y - childV.y*slope.x; // Sampo's test: project onto normal to "parent line" + // if (Abs(distance) < this->emHeight/2048.0 + 0.707107) // close enough to be considered "aligned"... + if (childDir.x*parentDir.x + childDir.y*parentDir.y > this->cosF1) + this->tt->ALIGNRP(ch); + else { + this->tt->AssertRounding(rdtg); + this->tt->MDRP(false,true,linkGrey,ch); + } + this->Touched(ch,this->tt->FVDir()); + } + } +} // TTSourceGenerator::Align +*****/ + +void TTSourceGenerator::Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[], size_t errMsgLen) { + TTVDirection dir; + short i; + + if (this->tt) { + dir = y ? yRomanDir : xRomanDir; // no [adjusted] italic directions... + this->tt->AssertFreeProjVector(dir); + if (knots > 1) this->tt->SLOOP(knots); + this->tt->SHPIX(knots,knot,amount); + for (i = 0; i < knots; i++) this->Touched(knot[i],dir); + } +} /* TTSourceGenerator::Move */ + +void TTSourceGenerator::Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[], size_t errorLen) { + short i,rp; + + if (this->tt) { + rp = this->tt->AssertEitherRefPointOnKnot(1,2,parent); + if (SameVectorsForAllChildren(projFreeVector,children)) { + this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[0]); + if (children <= 2) { // optimise for 2 or less shifted children + for (i = 0; i < children; i++) this->tt->SHP(rp,1,&child[i]); + } else { + this->tt->SLOOP(children); + this->tt->SHP(rp,children,child); + } + for (i = 0; i < children; i++) this->Touched(child[i],projFreeVector->fv[i].dir); + } else { + for (i = 0; i < children; i++) { + this->AssertFreeProjVector(&projFreeVector->pv,&projFreeVector->fv[i]);; + this->tt->SHP(rp,1,&child[i]); + this->Touched(child[i],projFreeVector->fv[i].dir); + } + } + } +} /* TTSourceGenerator::Shift */ + +short RectilinearDistanceOfDiagonal(bool x, const Vector V0, const Vector V1, const RVector strokeDirection) { + Vector link; + double dist; + + link = SubV(V1,V0); + dist = x ? link.x - link.y*strokeDirection.x/strokeDirection.y + : link.y - link.x*strokeDirection.y/strokeDirection.x; + return Abs((short)Round(dist)); +} // RectilinearDistanceOfDiagonal + +bool ClassifyStroke(Vector A1, Vector A2, Vector B1, Vector B2, short ppem, bool *crissCross, RVector *strokeDirection, bool *xLinks, short distance[], wchar_t error[], size_t errorLen) { + double cosF0; + int32_t sgn0,sgn1; + Vector aux; + RVector leftDirection,rightDirection; + + sgn0 = VectProdP(A1,B1,A1,A2); sgn0 = Sgn(sgn0); + sgn1 = VectProdP(A1,B1,A1,B2); sgn1 = Sgn(sgn1); + *crissCross = sgn0 != sgn1; + if (*crissCross) { aux = B1; B1 = B2; B2 = aux; } // we're criss-crossing; swap the second pair for further analysis + + leftDirection = RDirectionV(B1,A1); + rightDirection = RDirectionV(B2,A2); + cosF0 = cos(Rad(MAXSTROKESIDEANGLEDIFFERENCE)); + if (leftDirection.x*rightDirection.x + leftDirection.y*rightDirection.y < (ppem < 0 ? cosF0 : 2*cosF0*cosF0 - 1)) { // edges are "anti-parallel" || not nearly-parallel, RScalProdV(left,right);... + swprintf(error,errorLen,L"cannot accept (X|Y)STROKE (edges differ by %f degrees or more)",(double)(ppem < 0 ? MAXSTROKESIDEANGLEDIFFERENCE : 2*MAXSTROKESIDEANGLEDIFFERENCE)); + return false; + } + *strokeDirection = RAvgDirectionV(leftDirection,rightDirection); + *xLinks = Abs(strokeDirection->x) <= Abs(strokeDirection->y); + distance[0] = RectilinearDistanceOfDiagonal(*xLinks,A1,A2,*strokeDirection); + distance[1] = RectilinearDistanceOfDiagonal(*xLinks,B1,B2,*strokeDirection); + return true; // by now +} // ClassifyStroke + +void TTSourceGenerator::AssertStrokePhaseAngle(FVOverride fv, bool leftStationary[], short knot[]) { + short A1,B1,lsb; + wchar_t code[64]; + + // for the the italic stroke phase/angle adjustment to make sense, it always looks at the first knot pair, + // regardless of whether or not this will be the mirp side further down. However, in these cases linking + // criss-cross doesn't make sense, for it would undo the effects of an adjusted italic stroke phase. + + A1 = !leftStationary[0]; + B1 = 2+!leftStationary[1]; + if (fv != fvOldMethod && this->V[knot[B1]].y < this->V[knot[A1]].y) A1 = B1; // pull down the lower of the two parents + lsb = this->knots - PHANTOMPOINTS; + if (this->setItalicStrokePhase) { + if (this->V[knot[A1]].y != 0) { + if (fv != fvOldMethod && this->attrib[knot[A1]].touched[yRomanDir]) { // remember current y position + this->tt->AssertFreeProjVector(yRomanDir); + swprintf(code,sizeof(code)/sizeof(wchar_t),L"GC[N], %hi",knot[A1]); this->tt->Emit(code); + this->tt->Emit(L"#BEGIN"); // need to start new block or else pre-push interferes with GC + } + this->tt->AssertFreeProjVector(yItalDir); + this->tt->AssertRefPoint(0,lsb); + this->tt->ALIGNRP(knot[A1]); + this->Touched(knot[A1],yItalDir); + } + this->tt->AssertFreeProjVector(xRomanDir); // this used to be a call to fn 23, suggesting to be able to change the italic stroke + this->tt->AssertRounding(rtg); // phase globally. However, fn 23 can't be changed without changing the compiler, for + this->tt->MDAP(true,knot[A1]); // it has side effects which the compiler has to be aware of!!! + this->Touched(knot[A1],xRomanDir); + } + if (this->setItalicStrokeAngle) { + this->tt->AssertFreeProjVector(xAdjItalDir); + this->tt->AssertRefPoint(0,knot[A1]); + this->tt->ALIGNRP(knot[(A1+2)%4]); + this->Touched(knot[(A1+2)%4],xAdjItalDir); + if (this->V[knot[A1]].y != 0) { + this->tt->AssertFreeProjVector(yAdjItalDir); + this->tt->AssertRefPoint(0,lsb); + if (fv == fvOldMethod || !this->attrib[knot[A1]].touched[yRomanDir]) { + this->tt->MDRP(false,false,linkGrey,knot[A1]); + } else { // push it back up where it once belonged + this->tt->Emit(L"#END"); // end block started above + this->tt->AssertPVonCA(yRomanDir); + swprintf(code,sizeof(code)/sizeof(wchar_t),L"SCFS[], %hi, *",knot[A1]); this->tt->Emit(code); + } + this->Touched(knot[A1],yAdjItalDir); + } + } +} // TTSourceGenerator::AssertStrokePhaseAngle + +void TTSourceGenerator::Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[], size_t errorLen) { + if (fvOverride == fvOldMethod) { + short i,/*lsb,*/mirpSide,A2,A1,B2,B1,cvtIndex[2],distance[2]; + RVector strokeDirection; + bool crissCross,xLinks,iStroke; + wchar_t buf[64]; + + if (ClassifyStroke(this->V[knot[0]],this->V[knot[1]],this->V[knot[2]],this->V[knot[3]],ppem,&crissCross,&strokeDirection,&xLinks,distance,error,errorLen)) { + iStroke = strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y > this->cosF2; // angle between stroke and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION + for (i = 0; i < 4; i++) { + this->attrib[knot[i]].dStroke = true; + this->attrib[knot[i]].iStroke |= iStroke; + } + if (this->tt) { + for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,linkDiag,cvtStroke,Abs(distance[i])) : cvt; + if (cvtIndex[0] < 0 && cvtIndex[1] < 0) + swprintf(error,errorLen,L"cannot accept STROKE (no cvt number found)"); + else { + if (cvtIndex[0] < 0) cvtIndex[0] = cvtIndex[1]; + else if (cvtIndex[1] < 0) cvtIndex[1] = cvtIndex[0]; + mirpSide = Abs(distance[1]) > Abs(distance[0]) ? 1 : 0; // mirp the larger end + cvt = cvtIndex[mirpSide]; + + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* Stroke [%hi,%hi]%hi-[%hi,%hi]%hi */",knot[0],knot[1],distance[0],knot[2],knot[3],distance[1]); this->tt->Emit(buf); + + if (iStroke) this->AssertStrokePhaseAngle(fvOverride,leftStationary,knot); + + /***** + // for the the italic stroke phase/angle adjustment to make sense, it always looks at the first knot pair, + // regardless of whether or not this will be the mirp side further down. However, in these cases linking + // criss-cross doesn't make sense, for it would undo the effects of an adjusted italic stroke phase. + + A1 = !leftStationary[0]; + lsb = this->knots - PHANTOMPOINTS; + if (this->italicStrokePhase && iStroke) { + if (this->V[knot[A1]].y != 0) { + this->tt->AssertFreeProjVector(yItalDir); + this->tt->AssertRefPoint(0,lsb); + this->tt->ALIGNRP(knot[A1]); + this->Touched(knot[A1],yItalDir); + } + this->tt->AssertFreeProjVector(xRomanDir); // this used to be a call to fn 23, suggesting to be able to change the italic stroke + this->tt->AssertRounding(rtg); // phase globally. However, fn 23 can't be changed without changing the compiler, for + this->tt->MDAP(true,knot[A1]); // it has side effects which the compiler has to be aware of!!! + this->Touched(knot[A1],xRomanDir); + } + if (this->italicStrokeAngle && iStroke) { + this->tt->AssertFreeProjVector(xAdjItalDir); + this->tt->AssertRefPoint(0,knot[A1]); + this->tt->ALIGNRP(knot[A1+2]); + this->Touched(knot[A1+2],xAdjItalDir); + if (this->V[knot[A1]].y != 0) { + this->tt->AssertFreeProjVector(yAdjItalDir); + this->tt->AssertRefPoint(0,lsb); + this->tt->MDRP(false,false,linkGrey,knot[A1]); + this->Touched(knot[A1],yAdjItalDir); + } + } + *****/ + + // leftStationary[0] := [A1]--->[A2] + // leftStationary[1] := [B1]--->[B2] + // [B1] mirpSide := either 0 for [A1]<--->[A2] or 1 for [B1]<--->[B2] depending on which has the larger cvt entry (if any at all...) + // * *** + // * [B2] to understand all combinations, it is enough to assume STROKE([A1]--->[A2],[B1]--->[B2]) with the mirpSide = [A1]<--->[A2] + // * * the other combinations are taken care of by 4 assignments below. Then, what the Stroke does, is essentially: + // * * + // * * SDPTL[R] [A1],[(A1+2)%4] + // * * SFVTPV[] // either (A2 not assumed to be at a junction) + // * * SFVTL[r] [A2],neighbour // or (A2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction + // * * MDAP[r] [A1] + // * * DMIRP [A2] + // * * SDPTL[R] [(B1+2)%4],[B1] + // * * SFVTPV[] // again, either (B2 not assumed to be at a junction) + // * * SFVTL[r] [B2],neighbour // or (B2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction + // * *[A2] MDRP[mAssertVectorsAtPotentialJunction(dpv,knot[A1],knot[(A1+2)%4],knot[A2]); + this->tt->MDAP(false,knot[A1]); // don't round stationary point + this->Touched(knot[A1],this->tt->FVDir()); + this->tt->AssertRounding(rtg); + this->tt->AssertRefPoint(0,knot[A1]); + this->tt->AssertAutoFlip(true); + this->tt->DMIRP(knot[A2],cvt,knot[A1],knot[(A1+2)%4]); + this->Touched(knot[A2],this->tt->FVDir()); + + this->AssertVectorsAtPotentialJunction(dpv,knot[(B1+2)%4],knot[B1],knot[B2]); + // this->tt->AssertRoundingBelowPpem(rdtg,ppem); + this->tt->RoundDownToGridBelowPpem(ppem); + this->tt->AssertRefPoint(0,knot[(B2+2)%4]); + this->tt->MDRP(false,true,linkGrey,knot[B2]); + this->Touched(knot[B2],this->tt->FVDir()); + } + } + } + } else { // one of the new methods + short i,A2,A1,B2,B1,distance[2],refPoint0,refPoint1; + RVector strokeDirection; + double dotProd; + bool crissCross,xLinks,iStroke; + DiagParam *diagonal; + wchar_t buf[64]; + FVMTDirection fvmt; + + if (ClassifyStroke(this->V[knot[0]],this->V[knot[1]],this->V[knot[2]],this->V[knot[3]],ppem,&crissCross,&strokeDirection,&xLinks,distance,error,errorLen)) { + if (cvt < 0) + cvt = this->TheCvt(-1,-1,linkBlack,linkDiag,cvtStroke,Abs(distance[0])); + if (cvt < 0) + swprintf(error,errorLen,L"cannot accept DIAGONAL (no cvt number found)"); + else { + + // [B1] leftStationary[0] := [A1]--->[A2] + // * *** leftStationary[1] := [B1]--->[B2] + // * [B2] to understand all combinations, it is enough to assume DIAGONAL([A1]--->[A2],[B1]--->[B2]) + // * * the other combinations are taken care of by 4 assignments below. Then, what the DIAGONAL does, is essentially: + // * * + // * * SDPTL[R] [A1],[(A1+2)%4] + // * * SFV??[] // set freedom vector according to the 15 rules in ::AssertFVMT + // * * MDAP[r] [A1] + // * * DMIRP [A2] // mirpSide := always [A1]<--->[A2] per MT request + // * * SDPTL[R] [(B1+2)%4],[B1] + // * * SFV??[] // set freedom vector according to the 15 rules in ::AssertFVMT + // * * MDRP[mtt->Emit(buf); + + dotProd = strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y; + iStroke = Abs(dotProd) > this->cosF2; // angle between stroke and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION + if (iStroke) this->AssertStrokePhaseAngle(fvOverride,leftStationary,knot); + + A1 = !leftStationary[0]; + A2 = leftStationary[0]; + B1 = 2+!leftStationary[1]; + B2 = 2+ leftStationary[1]; + + // this->tt->AssertTTVonLine(dpv,knot[A1],knot[(A1+2)%4],this->V[knot[A1]],this->V[knot[(A1+2)%4]],true); + // this->AssertFVMT(knot[A1],knot[(A1+2)%4],knot[A2],strokeDirection); + // this->tt->MDAP(false,knot[A1]); // don't round stationary point ONLY NEEDED IF NOT TOUCHED A1 YET!!! + // this->Touched(knot[A1],this->tt->FVDir()); + fvmt = CalcDiagonalFVMT(fvOverride,knot[A1],knot[(A1+2)%4],knot[A2],strokeDirection,&refPoint0,&refPoint1); + /*****/ + if ((fvmt == fvOnY && !this->attrib[knot[A2]].touched[xRomanDir] && !this->xSmooth) || (fvmt == fvOnX && !this->attrib[knot[A2]].touched[yRomanDir] && !this->ySmooth)) { + this->tt->AssertRefPoint(0,knot[A1]); + this->tt->AssertFreeProjVector(fvmt == fvOnY ? xRomanDir : yRomanDir); + this->tt->MDRP(false,false,linkBlack,knot[A2]); + this->Touched(knot[A2],this->tt->FVDir()); + } + /*****/ + this->tt->AssertTTVonLine(dpv,knot[A1],knot[(A1+2)%4],this->V[knot[A1]],this->V[knot[(A1+2)%4]],true); + this->AssertFVMT(fvmt,refPoint0,refPoint1); + this->tt->AssertRounding(rtg); + this->tt->AssertRefPoint(0,knot[A1]); + this->tt->AssertAutoFlip(true); + this->tt->DMIRP(knot[A2],cvt,knot[A1],knot[(A1+2)%4]); + /// this->tt->MIRP(true,true,linkBlack,knot[A2],cvt,false); /// + this->Touched(knot[A2],this->tt->FVDir()); + + // this->tt->AssertTTVonLine(dpv,knot[(B1+2)%4],knot[B1],this->V[knot[(B1+2)%4]],this->V[knot[B1]],true); + // this->AssertFVMT(knot[(B1+2)%4],knot[B1],knot[B2],strokeDirection); + fvmt = CalcDiagonalFVMT(fvOverride,knot[(B1+2)%4],knot[B1],knot[B2],strokeDirection,&refPoint0,&refPoint1); + /*****/ + if ((fvmt == fvOnY && !this->attrib[knot[B2]].touched[xRomanDir] && !this->xSmooth) || (fvmt == fvOnX && !this->attrib[knot[B2]].touched[yRomanDir] && !this->ySmooth)) { + this->tt->AssertRefPoint(0,knot[B1]); + this->tt->AssertFreeProjVector(fvmt == fvOnY ? xRomanDir : yRomanDir); + this->tt->MDRP(false,false,linkBlack,knot[B2]); + this->Touched(knot[B2],this->tt->FVDir()); + } + /*****/ + this->tt->AssertTTVonLine(dpv,knot[(B1+2)%4],knot[B1],this->V[knot[(B1+2)%4]],this->V[knot[B1]],true); + this->AssertFVMT(fvmt,refPoint0,refPoint1); + // this->tt->AssertRoundingBelowPpem(rdtg,ppem); + this->tt->RoundDownToGridBelowPpem(ppem); + this->tt->AssertRefPoint(0,knot[(B2+2)%4]); + this->tt->MDRP(false,true,linkGrey,knot[B2]); + /// this->tt->AssertRoundingBelowPpem(rtg,ppem); /// + /// this->tt->AssertRefPoint(0,knot[B1]); /// + /// this->tt->MIRP(true,true,linkBlack,knot[B2],cvt,false); /// + this->Touched(knot[B2],this->tt->FVDir()); + + diagonal = new DiagParam; + for (i = 0; i < 2; i++) diagonal->leftStationary[i] = leftStationary[i]; + for (i = 0; i < 4; i++) diagonal->knot[i] = knot[i]; + this->diagonals->InsertAtEnd(diagonal); + } + } + } + *actualCvt = cvt; +} // TTSourceGenerator::Stroke + +/***** +void TTSourceGenerator::Stroke(bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[], size_t errorLen) { + short i,lsb,mirpSide,A2,A1,B2,B1,cvtIndex[2],distance[2]; + Vector link; + RVector leftDirection,rightDirection,strokeDirection; + bool iStroke; + double dist; + int32_t infoBits; + wchar_t buf[64]; + + leftDirection = RDirectionV(this->V[knot[2]],this->V[knot[0]]); + rightDirection = RDirectionV(this->V[knot[3]],this->V[knot[1]]); + if (leftDirection.x*rightDirection.x + leftDirection.y*rightDirection.y < (ppem < 0 ? this->cosF0 : 2*this->cosF0*this->cosF0 - 1)) { // edges are "anti-parallel" || not nearly-parallel, RScalProdV(left,right);... + swprintf(error,L"cannot accept (X|Y)STROKE (edges differ by %f degrees or more)",(double)(ppem < 0 ? MAXSTROKESIDEANGLEDIFFERENCE : 2*MAXSTROKESIDEANGLEDIFFERENCE)); + } else { // general stroke "action" command + for (i = 0; i < 4; i++) this->attrib[knot[i]].dStroke = true; + strokeDirection = RAvgDirectionV(leftDirection,rightDirection); + iStroke = strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y > this->cosF2; // angle between stroke and slope < MAXIMUM_ITALIC_ANGLE_DEVIATION + if (iStroke) { + for (i = 0; i < 4; i++) this->attrib[knot[i]].iStroke = true; + } + for (i = 0; i < 2; i++) { + link = SubV(this->V[knot[i*2+1]],this->V[knot[i*2]]); + dist = link.x*strokeDirection.y - link.y*strokeDirection.x; // project onto normal to stroke direction + distance[i] = (short)Round(dist); + } + if (this->tt) { + for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,linkDiag,cvtStroke,Abs(distance[i])) : cvt; + if (cvtIndex[0] < 0 && cvtIndex[1] < 0) + swprintf(error,L"cannot accept STROKE (no cvt number found)"); + else { + if (cvtIndex[0] < 0) cvtIndex[0] = cvtIndex[1]; + else if (cvtIndex[1] < 0) cvtIndex[1] = cvtIndex[0]; + mirpSide = Abs(distance[1]) > Abs(distance[0]) ? 1 : 0; // mirp the larger end + cvt = cvtIndex[mirpSide]; + + // for the the italic stroke phase/angle adjustment to make sense, it always looks at the first knot pair, + // regardless of whether or not this will be the mirp side further down. However, in these cases linking + // criss-cross doesn't make sense, for it would undo the effects of an adjusted italic stroke phase. + + A1 = !leftStationary[0]; + lsb = this->knots - PHANTOMPOINTS; + if (this->italicStrokePhase && iStroke) { + if (this->V[knot[A1]].y != 0) { + this->tt->AssertFreeProjVector(yItalDir); + this->tt->AssertRefPoint(0,lsb); + this->tt->ALIGNRP(knot[A1]); + this->Touched(knot[A1],yItalDir); + } + this->tt->AssertFreeProjVector(xRomanDir); // this used to be a call to fn 23, suggesting to be able to change the italic stroke + this->tt->AssertRounding(rtg); // phase globally. However, fn 23 can't be changed without changing the compiler, for + this->tt->MDAP(true,knot[A1]); // it has side effects which the compiler has to be aware of!!! + this->Touched(knot[A1],xRomanDir); + } + if (this->italicStrokeAngle && iStroke) { + this->tt->AssertFreeProjVector(xAdjItalDir); + this->tt->AssertRefPoint(0,knot[A1]); + this->tt->ALIGNRP(knot[A1+2]); + this->Touched(knot[A1+2],xAdjItalDir); + if (this->V[knot[A1]].y != 0) { + this->tt->AssertFreeProjVector(yAdjItalDir); + this->tt->AssertRefPoint(0,lsb); + this->tt->MDRP(false,false,linkGrey,knot[A1]); + this->Touched(knot[A1],yAdjItalDir); + } + } + + // leftStationary[0] := [A1]--->[A2] + // leftStationary[1] := [B1]--->[B2] + // [B1] mirpSide := either 0 for [A1]<--->[A2] or 1 for [B1]<--->[B2] depending on which has the larger cvt entry (if any at all...) + // * *** + // * [B2] to understand all combinations, it is enough to assume STROKE([A1]--->[A2],[B1]--->[B2]) with the mirpSide = [A1]<--->[A2] + // * * the other combinations are taken care of by 4 assignments below. Then, what the Stroke does, is essentially: + // * * + // * * SDPTL[R] [A1],[(A1+2)%4] + // * * SFVTPV[] // either (A2 not assumed to be at a junction) + // * * SFVTL[r] [A2],neighbour // or (A2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction + // * * MDAP[r] [A1] + // * * DMIRP [A2] + // * * SDPTL[R] [(B1+2)%4],[B1] + // * * SFVTPV[] // again, either (B2 not assumed to be at a junction) + // * * SFVTL[r] [B2],neighbour // or (B2 assumed to be at a junction, taken care of in AssertVectorsAtPotentialJunction + // * *[A2] MDRP[mtt->Emit(buf); + + A1 = mirpSide*2+!leftStationary[ mirpSide]; + A2 = mirpSide*2+ leftStationary[ mirpSide]; + B1 = !mirpSide*2+!leftStationary[!mirpSide]; + B2 = !mirpSide*2+ leftStationary[!mirpSide]; + + this->AssertVectorsAtPotentialJunction(dpv,knot[A1],knot[(A1+2)%4],knot[A2]); + this->tt->MDAP(false,knot[A1]); // don't round stationary point + this->Touched(knot[A1],this->tt->FVDir()); + this->tt->AssertRounding(rtg); + this->tt->AssertRefPoint(0,knot[A1]); + this->tt->AssertAutoFlip(true); + this->tt->DMIRP(knot[A2],cvt,knot[A1],knot[(A1+2)%4]); + this->Touched(knot[A2],this->tt->FVDir()); + + this->AssertVectorsAtPotentialJunction(dpv,knot[(B1+2)%4],knot[B1],knot[B2]); + this->tt->AssertRoundingBelowPpem(rdtg,ppem); + this->tt->AssertRefPoint(0,knot[(B2+2)%4]); + this->tt->MDRP(false,true,linkGrey,knot[B2]); + this->Touched(knot[B2],this->tt->FVDir()); + } + } else { + infoBits = 0; + if (this->italic && strokeDirection.x*this->slope.x + strokeDirection.y*this->slope.y > this->cosF5) { // Garamond Italic 156 ??? + if (this->charCode == 'I') infoBits = UCISTROKE; + else if (this->charCode == 'l') infoBits = LClSTROKE; // lower case 'L', that is... + else if (this->charCode == '1') infoBits = FIGSTROKE; + } + this->EnterCvt(knot[0],knot[1],(LinkColor)-1,linkDiag,cvtStroke,distance[0],true,0); + this->EnterCvt(knot[2],knot[3],(LinkColor)-1,linkDiag,cvtStroke,distance[1],true,infoBits); + } + } + *actualCvt = cvt; +} // TTSourceGenerator::Stroke +*****/ + +void TTSourceGenerator::DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[], size_t errorLen) { + RVector strokeDirection,fixedDirection; + short i,distance[2],cvtIndex[2]; + bool xLinks,flip; + wchar_t buf[64]; + + strokeDirection = RAvgDirectionV(RDirectionV(this->V[knot[2]],this->V[knot[0]]),RDirectionV(this->V[knot[3]],this->V[knot[1]])); + xLinks = Abs(strokeDirection.x) <= Abs(strokeDirection.y); + if (leftStationary[0] != leftStationary[1]) { // Links from opposite sides + fixedDirection = RDirectionV(this->V[knot[2+!leftStationary[1]]],this->V[knot[!leftStationary[0]]]); + fixedDirection.x = Abs(fixedDirection.x); + fixedDirection.y = Abs(fixedDirection.y); + /* if we are within diagFudge of xAxis || yAxis, it seems to be better to define xLink by the fixedDirection... */ + if (fixedDirection.y < fixedDirection.x*this->tanF4 || fixedDirection.x < fixedDirection.y*this->tanF4) xLinks = fixedDirection.x <= fixedDirection.y; + } + for (i = 0; i < 2; i++) distance[i] = RectilinearDistanceOfDiagonal(xLinks,this->V[knot[i*2]],this->V[knot[i*2+1]],strokeDirection); + flip = distance[0] < 0; // the other distance doesn't seem to matter... + + if (this->tt) { + for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,xLinks ? linkX : linkY,cvtNewDiagStroke,Abs(distance[i])) : cvt; + if (cvtIndex[0] < 0 && cvtIndex[1] < 0) + swprintf(error,errorLen,L"cannot accept DSTROKE (no cvt number found)"); + else { + if (cvtIndex[0] < 0) cvt = cvtIndex[1]; + else if (cvtIndex[1] < 0) cvt = cvtIndex[0]; + else if (Abs(distance[0]) < Abs(distance[1])) cvt = cvtIndex[1]; + else cvt = cvtIndex[0]; + + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* DStroke [%hi,%hi]%hi-[%hi,%hi]%hi */",knot[0],knot[1],distance[0],knot[2],knot[3],distance[1]); this->tt->Emit(buf); + this->tt->AssertAutoFlip(false); + this->tt->CALL656(leftStationary[0] != leftStationary[1],knot[ !leftStationary[0]],knot[ leftStationary[0]], + knot[2+!leftStationary[0]],knot[2+leftStationary[0]],cvt,ND_HEIGHT_STORE_1,xLinks,leftStationary[0] ? flip : !flip); + this->Touched(knot[ leftStationary[0]],diagDir); // if the left point is stationary, then the 0+leftStationary[0]'th point is floating... + this->Touched(knot[2+leftStationary[1]],diagDir); + } + } + *actualCvt = cvt; +} /* TTSourceGenerator::DStroke */ + +void TTSourceGenerator::IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[], size_t errorLen) { +/***** leftStationary[] are not used for anything currently, linking is always done left-to-right, or leftStationary[0] == leftStationary[1] == true *****/ + RVector strokeDirection; + short i,distance[2],cvtIndex[2],parent,child; + bool xLinks,flip[2],move[4]; + wchar_t buf[64]; + + strokeDirection = RAvgDirectionV(RDirectionV(this->V[knot[2]],this->V[knot[0]]),RDirectionV(this->V[knot[3]],this->V[knot[1]])); + xLinks = Abs(strokeDirection.x) <= Abs(strokeDirection.y); + if (xLinks) { + for (i = 0; i < 2; i++) { + distance[i] = RectilinearDistanceOfDiagonal(xLinks,this->V[knot[i*2]],this->V[knot[i*2+1]],strokeDirection); + flip[i] = distance[i] < 0; + } + if (this->tt) { + for (i = 0; i < 2; i++) cvtIndex[i] = cvt < 0 ? this->TheCvt(-1,-1,linkBlack,xLinks ? linkX : linkY,cvtNewDiagStroke,Abs(distance[i])) : cvt; + if (cvtIndex[0] < 0 && cvtIndex[1] < 0) + swprintf(error,errorLen,L"cannot accept ISTROKE (no cvt number found)"); + else { + if (cvtIndex[0] < 0) cvt = cvtIndex[1]; + else if (cvtIndex[1] < 0) cvt = cvtIndex[0]; + else if (Abs(distance[0]) < Abs(distance[1])) cvt = cvtIndex[1]; + else cvt = cvtIndex[0]; + + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* IStroke [%hi,%hi]%hi-[%hi,%hi]%hi */",knot[0],knot[1],distance[0],knot[2],knot[3],distance[1]); this->tt->Emit(buf); + + for (i = 0; i < 4; i++) { + move[i] = this->attrib[knot[i]].cvt != height[i/2]; + if (move[i]) { + this->tt->CALL678(false,knot[i],knot[(i+2)%4],height[i/2],ND_ITALIC_STORE_1 + i); + this->Touched(knot[i],diagDir); + } + } + this->tt->AssertFreeProjVector(xRomanDir); + this->tt->AssertSuperRounding(1,phase,4); + this->tt->MDAP(true,knot[0]); + this->Touched(knot[0],xRomanDir); + this->tt->AssertAutoFlip(false); + this->tt->CALL64(knot[0],knot[2],RESERVED_HEIGHTSPACE_START, phase & 1,false); + for (i = 0; i <= 1; i++) { + parent = knot[i*2]; child = knot[i*2+1]; + this->tt->CALL64(parent,child,cvt,deltaRounding[this->attrib[parent].round[false]][this->attrib[child].round[false]] == rthg,flip[i]); + } + for (i = 1; i <= 3; i++) this->Touched(knot[i],xRomanDir); + for (i = 0; i < 4; i++) + if (move[i]) + this->tt->CALL678(true,knot[i],knot[(i+2)%4],height[i/2],ND_ITALIC_STORE_1 + i); + } + } + } else { + swprintf(error,errorLen,L"cannot accept ISTROKE (can be used for italic strokes only)"); + } + *actualCvt = cvt; +} /* TTSourceGenerator::IStroke */ + +void TTSourceGenerator::FixDStrokes(void) { + TTVDirection fv,pv,dir[2]; + short i,j; + Attribute *attrib; + + if (this->tt) { + fv = this->tt->FVDir(); + pv = this->tt->PVDir(); + if (fv == xRomanDir && pv == xRomanDir) { + dir[0] = xRomanDir; dir[1] = yRomanDir; + } else { + dir[0] = yRomanDir; dir[1] = xRomanDir; + } + for (i = 0; i < 2; i++) { + for (j = 0; j < this->knots - PHANTOMPOINTS; j++) { + attrib = &this->attrib[j]; + if ((attrib->dStroke || attrib->iStroke) && !attrib->touched[dir[i]]) { + this->tt->AssertFreeProjVector(dir[i]); + this->tt->MDAP(false,j); + } + } + } + } +} /* TTSourceGenerator::FixDStrokes */ + +void TTSourceGenerator::Serif(bool forward, short type, short knots, short knot[], wchar_t error[], size_t errorLen) { + short ppem[1] = {1},actualCvt; + F26Dot6 dist[1] = {one6}; + wchar_t buf[64]; + + switch (type) { + case 0: /***** + + ****[0]**** [2]**[3] + **** **** ** * + ****[1]** * + ****[6]**** * + **** ****** * + **** * + *** * + ** * + * * + * * + * * + * * + * * + * * + * * + [5]**[4] + + *****/ + if (this->tt) { + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* Round serif %hi %hi %hi %hi %hi %hi %hi */",knot[0],knot[1],knot[2],knot[3],knot[4],knot[5],knot[6]); this->tt->Emit(buf); + this->Link(true, false,&this->yRomanPV,false,knot[0],knot[1],cvtSerifOther,invalidCvtNum,1,ppem,dist,&actualCvt,error, errorLen); + this->Link(true, false,&this->yRomanPV,false,knot[0],knot[3],cvtSerifOther,invalidCvtNum,0,NULL,NULL,&actualCvt,error, errorLen); + this->Link(true, false,&this->yRomanPV,false,knot[3],knot[4],cvtSerifOther,invalidCvtNum,1,ppem,dist,&actualCvt,error, errorLen); + this->Link(false,false,&this->xRomanPV,false,knot[4],knot[5],cvtSerifThin, invalidCvtNum,1,ppem,dist,&actualCvt,error, errorLen); + this->Link(false,false,&this->xRomanPV,false,knot[4],knot[3],cvtSerifOther,invalidCvtNum,0,NULL,NULL,&actualCvt,error, errorLen); + this->Link(false,false,&this->xRomanPV,false,knot[3],knot[2],cvtSerifThin, invalidCvtNum,1,ppem,dist,&actualCvt,error, errorLen); + } + break; + case 1: /***** + + ****[3] + **** * + **** * + **** * + **** * + [2]* **** * + * *** ** * + * ** * * + [1]* * * + * * + * * + * * + * * + * * + [0] * + + + *****/ + if (this->tt) { + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* Triangular serif %hi %hi %hi %hi */",knot[0],knot[1],knot[2],knot[3]); this->tt->Emit(buf); + this->Link(true, false,&this->yRomanPV,false,knot[3],knot[2],cvtSerifCurveHeight,invalidCvtNum,0,NULL,NULL,&actualCvt,error,errorLen); + this->Link(true, false,&this->yRomanPV,false,knot[2],knot[1],cvtSerifThin, invalidCvtNum,1,ppem,dist,&actualCvt,error,errorLen); + this->Link(true, false,this->attrib[knot[0]].iStroke ? &this->yAdjItalPV : &this->yRomanPV,false,knot[2],knot[0],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error,errorLen); + // set vectors to yAdjItalDir if knot[0] is part of an iStroke... + this->Link(false,false,&this->xRomanPV,false,knot[0],knot[2],cvtSerifExt, invalidCvtNum,0,NULL,NULL,&actualCvt,error,errorLen); + } + break; + case 2: + case 3: /***** + + [3] * * [3] [3] * * [3] + * * * * * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * + * * * * * ** * ** + *** *** *** *** * **** * **** + [2]**** ******* ******* ****[2] [2]**** ******** ******* *****[2] + * * * * * * * * + * * * * * * * * + [1]******************************[0] [0]******************************[1] [1]******************************[0] [0]******************************[1] + + type 2, horzBase, forward type 2, horzBase, !forward type 3, horzBase, forward type 3, horzBase, !forward + + + ****[0] [2]*[1] ****[0] [2]*[1] + * * * * * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * + *** * *** * *** * *** * + *********** * [3]******** * *********** * [3]******** * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + [3]******** * *********** * [3]******** * *********** * + *** * *** * *** * *** * + * * * * * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * + [2]*[1] ****[0] [2]*[1] ****[0] + + type 2, vertBase, forward type 2, vertBase, !forward type 2, italBase, forward type 2, italBase, !forward + + *****/ { + bool horzBase,vertBase,italBase; + RVector base; + double absVectProd,length; + short cvt[3],fun; + + base.x = this->V[knot[1]].x - this->V[knot[0]].x; + base.y = this->V[knot[1]].y - this->V[knot[0]].y; + length = LengthR(base); + absVectProd = base.x*this->xAxis.y - base.y*this->xAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... could do like nearHorz/Vert though... + horzBase = absVectProd < length*this->sinF3; // angle between base and xAxis < serifFudge + absVectProd = base.x*this->yAxis.y - base.y*this->yAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... and therefore need no xAxis/yAxis!!! + vertBase = absVectProd < length*this->sinF3; // angle between base and yAxis < serifFudge + absVectProd = base.x*this->slope.y - base.y*this->slope.x; absVectProd = Abs(absVectProd); // RVectProdV... + italBase = this->italic && absVectProd < length*this->sinF22; // angle between base and yAxis < 2*MAXIMUM_ITALIC_ANGLE_DEVIATION + if (vertBase || horzBase || italBase) { + if (this->tt) { + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* " WIDE_STR_FORMAT L" serif %hi %hi %hi */",forward ? L"Forward" : L"Backward",knot[1],knot[2],knot[3]); this->tt->Emit(buf); + if (horzBase && type == 2) { // seems to be some kind of optimisation for the frequent case of a simple serif such as in a Times 'I'... + cvt[0] = this->TheCvt(knot[3],knot[1],linkGrey,linkX,cvtSerifExt, -1); + cvt[1] = this->TheCvt(knot[1],knot[2],linkGrey,linkY,cvtSerifThin, -1); + cvt[2] = this->TheCvt(knot[1],knot[3],linkGrey,linkY,cvtSerifHeight,-1); + if (cvt[0] < 0) + swprintf(error,errorLen,L"cannot accept SERIF (need cvt numbers for XLINK(%hi,%hi))",knot[3],knot[1]); + else if (cvt[1] < 0) + swprintf(error,errorLen,L"cannot accept SERIF (need cvt numbers for YLINK(%hi,%hi))",knot[1],knot[2]); + else if (cvt[2] < 0) + swprintf(error,errorLen, L"cannot accept SERIF (need cvt numbers for YLINK(%hi,%hi))",knot[1],knot[3]); + else { + short jumpPpemSize[1]; + F26Dot6 pixelSize[1]; + + jumpPpemSize[0] = 1; + pixelSize[0] = one6; + + this->tt->AssertMinDist(1,jumpPpemSize,pixelSize); + this->tt->AssertRounding(rtg); + this->tt->AssertAutoFlip(true); + + if (knot[2] == knot[1]+1) fun = 34; + else if (knot[2] == knot[1]-1) fun = 35; + else fun = 36; + this->tt->CALL3456(fun,knot[3],cvt[2],knot[2],cvt[1],knot[1],cvt[0]); + /* this doesn't update the knots as being touched, but since this is only relevant for STROKEs that are + neither horizontal nor vertical, and the present serif belongs to a vertical stroke, we couldn't care less... */ + } + + + } else { + if (italBase) this->Anchor(false,&this->xRomanPV,knot[1],invalidCvtNum,false,error,errorLen); + this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[2],cvtSerifThin,invalidCvtNum,1,ppem,dist,&actualCvt,error,errorLen); + if (type == 2) this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[3],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error,errorLen); // control serif height + this->Link(!horzBase,this->attrib[knot[3]].dStroke || this->attrib[knot[3]].iStroke,!horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[3],knot[1],cvtSerifExt,invalidCvtNum,0,NULL,NULL,&actualCvt,error,errorLen); + } + } + } else + swprintf(error,errorLen,L"cannot accept SERIF (base differs from horizontal or vertical by %f degrees or more, or from italic angle by %f degrees or more)", + (double)serifFudge,(double)2*MAXIMUM_ITALIC_ANGLE_DEVIATION); + break; + } + case 4: /***** + + [5] * * [5] + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + **[3]******[4] ************ ************ [4]******[3]** + * * * * + [2] * * [2] + * * * * + **[1]**********************[0]** **[0]**********************[1]** + + forward !forward plus the same variations as in cases 2 and 3 + + *****/ { + bool horzBase,vertBase,italBase; + RVector base; + double absVectProd,length; + Vector /***** link21,link23,*****/link34; + + base.x = this->V[knot[1]].x - this->V[knot[0]].x; + base.y = this->V[knot[1]].y - this->V[knot[0]].y; + length = LengthR(base); + absVectProd = base.x*this->xAxis.y - base.y*this->xAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... + horzBase = absVectProd < length*this->sinF3; // angle between base and xAxis < serifFudge + absVectProd = base.x*this->yAxis.y - base.y*this->yAxis.x; absVectProd = Abs(absVectProd); // RVectProdV... + vertBase = absVectProd < length*this->sinF3; // angle between base and yAxis < serifFudge + absVectProd = base.x*this->slope.y - base.y*this->slope.x; absVectProd = Abs(absVectProd); // RVectProdV... + italBase = this->italic && absVectProd < length*this->sinF22; // angle between base and yAxis < 2*MAXIMUM_ITALIC_ANGLE_DEVIATION + if (vertBase || horzBase || italBase) { +/* universal serif used by the new feature recognition */ + if (this->tt) { + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* " WIDE_STR_FORMAT L" univ-serif %hi %hi %hi %hi */",forward ? L"Forward" : L"Backward",knot[1],knot[2],knot[3],knot[4]); this->tt->Emit(buf); + + this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[3],cvtSerifThin,invalidCvtNum,1,ppem,dist,&actualCvt,error, errorLen); + this->Link(!horzBase,false,!horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[4],knot[2],cvtSerifExt,invalidCvtNum,0,NULL,NULL,&actualCvt,error, errorLen); + link34 = SubV(this->V[knot[3]],this->V[knot[4]]); + if (horzBase) { + if ((Abs(link34.x) > 0)) { // could compare with a small value + this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[4],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error, errorLen); + } + } else { + if ((Abs(link34.y) > 0)) { + this->Link(horzBase,false,horzBase ? &this->yRomanPV : &this->yRomanPV,false,knot[1],knot[4],cvtSerifHeight,invalidCvtNum,1,ppem,dist,&actualCvt,error, errorLen); + } + } + } + } else + swprintf(error,errorLen,L"cannot accept SERIF (base differs from horizontal or vertical by %f degrees or more, or from italic angle by %f degrees or more)", + (double)serifFudge,(double)2*MAXIMUM_ITALIC_ANGLE_DEVIATION); + break; + } + } +} /* TTSourceGenerator::Serif */ + +void TTSourceGenerator::Scoop(short parent0, short child, short parent1, wchar_t error[], size_t errorLen) { + Vector base; + bool ok,y = false; + short dist,actualCvt; + + base = SubV(this->V[parent1],this->V[parent0]); + base.x = Abs(base.x); + base.y = Abs(base.y); + ok = true; + if (base.y <= base.x*this->tanF) { dist = (short)base.y; y = true; } // near horizontal + else if (base.x <= base.y*this->tanF) { dist = (short)base.x; y = false; } // near vertical + else { + ok = false; + swprintf(error,errorLen,L"cannot accept SCOOP (base differs from horizontal or vertical by %f degrees or more)",(double)strokeFudge); + } + if (ok) + if (this->tt) this->Link(y,false,y ? &this->yRomanPV : &this->yRomanPV,false,parent0,child,cvtScoopDepth,invalidCvtNum,0,NULL,NULL,&actualCvt,error, errorLen); +} /* TTSourceGenerator::Scoop */ + +void TTSourceGenerator::Smooth(short y, short italicFlag) { + short xyDir,contour,start,end,n,parent0,parent1,children; + TTVDirection dir; + + if (this->tt) { + if (y < 0) { // do Y and X unless legacy then do X and Y + if (this->legacyCompile) { + this->Smooth(0, italicFlag); + this->Smooth(1, -1); + }else { + this->Smooth(1, -1); + this->Smooth(0, italicFlag); + } + } else { // Y or X only + if (italicFlag < 0) { + this->tt->IUP(y == 1); + } else { + + xyDir = y == 0 ? xRomanDir : yRomanDir; + dir = y == 0 ? (italicFlag == 0 ? xItalDir : xAdjItalDir) : (italicFlag == 0 ? yItalDir : yAdjItalDir); + + this->tt->AssertFreeProjVector(dir); + + for (contour = 0; contour < this->glyph->numContoursInGlyph; contour++) { + start = this->glyph->startPoint[contour]; + end = this->glyph->endPoint[contour]; + n = (end - start + 1); + for (parent0 = start; parent0 <= end && !this->attrib[parent0].touched[xyDir]; parent0++); + while (parent0 <= end) { // => this->attrib[parent0].touched[xyDir] + parent1 = (parent0-start+1)%n + start; + children = 0; + while (!this->attrib[parent1].touched[xyDir]) { + children++; + parent1 = (parent1-start+1)%n + start; + } + if (parent0 != parent1 && children > 0) { + this->tt->IPRange(y == 1,parent0,parent1,start,end); + } + parent0 = parent0 + children + 1; + } + } + } + if (y == 1) + this->ySmooth = true; + else + this->xSmooth = true; + } + } +} /* TTSourceGenerator::Smooth */ + +void TTSourceGenerator::VacuFormLimit(short ppem) { + this->vacuFormLimit = ppem; +} /* TTSourceGenerator::VacuFormLimit */ + +void TTSourceGenerator::VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[], size_t errorLen) { + short i,cvt; + VacuFormParams *vacu; + + if (this->tt) { + // if (this->font->controlRounds || this->font->onlyControlLCBranches && this->charGroup == lowerCase && type == 2) { + if (this->vacuForms < maxVacuForms) { + cvt = this->TheCvt(-1,-1,linkGrey,linkAnyDir,type == 2 ? cvtBranchBendRadius : cvtBendRadius,radius); + if (cvt >= 0) { + vacu = &this->vacuForm[this->vacuForms]; + vacu->type = type; + vacu->radius = radius; + vacu->cvt = cvt; + for (i = 0; i < 2; i++) vacu->forward[i] = forward[i]; + for (i = 0; i < 4; i++) vacu->knot[i] = knot[i]; + this->vacuForms++; + } else + swprintf(error,errorLen,L"cannot accept VACUFORMROUND (no cvt number found)"); // if we have to test this at all!!! + } else + swprintf(error,errorLen,L"too many VACUFORMROUND commands (cannot have more than %li VACUFORMROUNDs)",maxVacuForms); + // } + } +} /* TTSourceGenerator::VacuFormRound */ + +void TTSourceGenerator::Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[], size_t errMsgLen) { + if (this->tt) { + this->tt->AssertFreeProjVector(y ? yRomanDir : xRomanDir); // no [adjusted] italic directions... + this->tt->DLT(false,color,knot,amount,ppemSize); + } +} /* TTSourceGenerator::Delta */ + +void TTSourceGenerator::Call(short actParams, short anyNum[], short functNum) { + if (this->tt) this->tt->CALL(actParams,anyNum,functNum); +} /* TTSourceGenerator::Call */ + +void TTSourceGenerator::Asm(bool inLine, wchar_t text[], wchar_t error[], size_t errorLen) { + if (this->tt) this->tt->ASM(inLine,text); +} /* TTSourceGenerator::Asm */ + +void TTSourceGenerator::Quit(void) { + // nix +} /* TTSourceGenerator::Quit */ + +void InitFreeProjVector(TTVDirection pv, ProjFreeVector *projFreeVector) { + short i; + + projFreeVector->pv.dir = pv; + projFreeVector->pv.from = projFreeVector->pv.to = invalidKnotNum; + for (i = 0; i < maxParams; i++) { + projFreeVector->fv[i].dir = pv; + projFreeVector->fv[i].from = projFreeVector->fv[i].to = invalidKnotNum; + } +} // InitFreeProjVector + +void TTSourceGenerator::InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError) { + short i,j,n,cont; + double vectProd,deg; + Attribute *attrib; + Vector V[3],D[2]; + RVector T[2]; + wchar_t dateTime[32],buf[128]; + + this->legacyCompile = legacyCompile; + + InitFreeProjVector(xRomanDir, &this->xRomanPV); + InitFreeProjVector(yRomanDir, &this->yRomanPV); + InitFreeProjVector(xItalDir, &this->xItalPV); + InitFreeProjVector(yItalDir, &this->yItalPV); + InitFreeProjVector(xAdjItalDir,&this->xAdjItalPV); + InitFreeProjVector(yAdjItalDir,&this->yAdjItalPV); + + this->xAxis.x = 1; this->yAxis.x = 0; this->slope.x = 0; + this->xAxis.y = 0; this->yAxis.y = 1; this->slope.y = 1; + this->riseCvt = this->runCvt = invalidCvtNum; + this->cosF = cos(Rad(strokeFudge)); + this->tanF = tan(Rad(strokeFudge)); + this->cosF0 = cos(Rad(MAXSTROKESIDEANGLEDIFFERENCE)); + this->cosF1 = cos(Rad(ALIGN_TOLERANCE)); // more fudge factors... + this->cosF2 = cos(Rad(MAXIMUM_ITALIC_ANGLE_DEVIATION)); + this->sinF22 = sin(Rad(2*MAXIMUM_ITALIC_ANGLE_DEVIATION)); + this->sinF3 = sin(Rad(serifFudge)); + this->tanF4 = tan(Rad(diagFudge)); + this->cosF5 = cos(Rad(gmndItFudge)); + this->cosF6 = cos(Rad(neighFudge)); + this->cosMT = cos(Rad(diagFudgeMT)); + this->tanMT = tan(Rad(diagFudgeMT)); + + this->font = font; + this->glyph = glyph; + this->glyphIndex = glyphIndex; this->charCode = this->font->CharCodeOf(this->glyphIndex); + + this->charGroup = this->font->CharGroupOf(this->glyphIndex); + + this->knots = (glyph->numContoursInGlyph ? glyph->endPoint[glyph->numContoursInGlyph - 1] + 1 : 0) + PHANTOMPOINTS; + + this->attrib = (AttributePtr)NewP(this->knots*sizeof(Attribute)); + *memError = this->attrib == NULL; + this->V = (VectorPtr)NewP(this->knots*sizeof(Vector)); + *memError = *memError || this->V == NULL; + this->diagonals = new LinearListStruct; + this->aligns = new LinearListStruct; + *memError = *memError || this->diagonals == NULL || this->aligns == NULL; + if (*memError) return; + + this->vacuFormLimit = -1; + this->vacuForms = 0; + + this->tt = tt; + + for (i = 0; i < this->knots; i++) { + this->V[i].x = glyph->x[i]; + this->V[i].y = glyph->y[i]; + } + + for (cont = 0; cont < glyph->numContoursInGlyph; cont++) { + i = glyph->startPoint[cont]; + n = glyph->endPoint[cont] - i + 1; + V[0] = this->V[i+n-1]; V[1] = this->V[i]; D[0] = SubV(V[1],V[0]); T[0] = RDirectionV(V[1],V[0]); + for (j = 0; j < n; j++) { + V[2] = this->V[i + (j + 1)%n]; D[1] = SubV(V[2],V[1]); T[1] = RDirectionV(V[2],V[1]); + vectProd = T[0].x*T[1].y - T[0].y*T[1].x; + vectProd = Max(-1,Min(vectProd,1)); // force asin's arg into [-1..1] + deg = Deg(asin(vectProd)); + attrib = &this->attrib[i + j]; + attrib->deltaAngle = (short)Round(deg); + V[0] = V[1]; V[1] = V[2]; T[0] = T[1]; D[0] = D[1]; + } + } + + this->InitCodePathState(); + + if (this->tt) { + DateTimeStrg(dateTime); + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* TT glyph %i, char 0x%lx",this->glyphIndex,this->charCode); + if (L' ' < this->charCode && this->charCode < 0x7f) { swprintf(&buf[STRLENW(buf)],sizeof(buf)/sizeof(wchar_t),L" (%c)",(wchar_t)this->charCode); } + swprintf(&buf[STRLENW(buf)],sizeof(buf)/sizeof(wchar_t),L" */"); + this->tt->Emit(buf); + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"/* VTT " WIDE_STR_FORMAT L" compiler " WIDE_STR_FORMAT L" */",VTTVersionString,dateTime); this->tt->Emit(buf); + } +} /* TTSourceGenerator::InitTTGenerator */ + +void TTSourceGenerator::InitCodePathState(void) { + short i,j; + Attribute *attrib; + + this->italic = false; + this->mainStrokeAngle = this->glyphStrokeAngle = this->setItalicStrokePhase = this->setItalicStrokeAngle = false; + this->xSmooth = this->ySmooth = false; + this->leftAnchor = this->rightAnchor = -1; + + for (i = 0; i < this->knots; i++) { + attrib = &this->attrib[i]; + for (j = 0; j < 2; j++) attrib->round[j] = rtg; // default + attrib->cvt = invalidCvtNum; + for (j = 0; j < 2; j++) attrib->touched[j] = false; + attrib->dStroke = false; + attrib->iStroke = false; + attrib->on = glyph->onCurve[i]; + attrib->vacu = false; + } +} // TTSourceGenerator::InitCodePathState + +void TTSourceGenerator::TermCodePathState(void) { + if (this->tt) this->DoVacuFormRound(); +} // TTSourceGenerator::TermCodePathState + +void TTSourceGenerator::TermTTGenerator(void) { + this->TermCodePathState(); + if (this->aligns) delete this->aligns; + if (this->diagonals) delete this->diagonals; + if (this->V) DisposeP((void**)&this->V); + if (this->attrib) DisposeP((void**)&this->attrib); +} /* TTSourceGenerator::TermTTGenerator */ + +TTSourceGenerator::TTSourceGenerator(void) { + /* nix */ +} /* TTSourceGenerator::TTSourceGenerator */ + +TTSourceGenerator::~TTSourceGenerator(void) { + /* nix */ +} /* TTSourceGenerator::~TTSourceGenerator */ + +void TTSourceGenerator::AssertFreeProjVector(const TTVectorDesc *pv, const TTVectorDesc *fv) { + if (pv->dir < diagDir && fv->dir < diagDir && pv->dir == fv->dir) + this->tt->AssertFreeProjVector(pv->dir); + else if (fv->dir == yItalDir || fv->dir == yAdjItalDir) { // first assert fv because fn call overwrites pv too... + this->tt->AssertFreeProjVector(fv->dir); + if (pv->dir < diagDir) { + this->tt->AssertPVonCA(pv->dir == yRomanDir); + } else { + this->tt->AssertTTVonLine(dpv,pv->from,pv->to,this->V[pv->from],this->V[pv->to],pv->dir == perpDiagDir); + } + } else if (pv->dir == xItalDir || pv->dir == xAdjItalDir) { // first assert pv because fn call overwrites fv too... + this->tt->AssertFreeProjVector(pv->dir); + if (fv->dir < diagDir) { + this->tt->AssertFVonCA(fv->dir == yRomanDir); + } else { + this->tt->AssertTTVonLine(::fv,fv->from,fv->to,this->V[fv->from],this->V[fv->to],fv->dir == perpDiagDir); + } + } else { + if (pv->dir < diagDir) { + this->tt->AssertPVonCA(pv->dir == yRomanDir); + } else { + this->tt->AssertTTVonLine(dpv,pv->from,pv->to,this->V[pv->from],this->V[pv->to],pv->dir == perpDiagDir); + } + if (fv->dir < diagDir) { + this->tt->AssertFVonCA(fv->dir == yRomanDir); + } else { + this->tt->AssertTTVonLine(::fv,fv->from,fv->to,this->V[fv->from],this->V[fv->to],fv->dir == perpDiagDir); + } + } +} // TTSourceGenerator::AssertFreeProjVector + +void TTSourceGenerator::CondRoundInterpolees(bool y, short children, short child[], Rounding actual[], Rounding targeted) { + short i,knot; + bool negativeDist; + + for (i = 0; i < children; i++) { + if (actual[i] == targeted) { + // same roundabout way as in Link to maintain philosophy that knot gets rounded, not (absolute or relative) distance + knot = child[i]; + negativeDist = y ? this->V[knot].y < 0 : this->V[knot].x < 0; + if (negativeDist && rdtg <= targeted && targeted <= rutg) targeted = (Rounding)(((short)targeted - (short)rdtg + 1)%2 + (short)rdtg); // rdtg <=> rutg + + this->tt->AssertRounding(targeted); // this will set the rounding method only once per targeted rounding method, if once at all... + this->tt->MDAP(true,knot); + } + } +} /* TTSourceGenerator::CondRoundInterpolees */ + +short TTSourceGenerator::Neighbour(short parent0, short parent1, short child, bool immediate) { + short cont,base,n,inc,dec,pred,succ,temp; + RVector predD,succD,tempD,parentD; + double predP,succP; + + /***** + + along contour, pick child's neighbours (one of the parents or the neighbour we're looking for) + and select the one with larger angle to "parent line". Notice that the neighbour may actually + be the neighbour's neighbour, if it is too close, and will be taken from neighbour's neighbour + along the contour as long as it is not too far away and its angle is within neighFudge degrees... + The problem here seems to be that we would like to set the FV onto the line [neighbour]-[child], + but these two may not be where they used to be due to previous instructions, so we attempt to + find a neighbour which is far enough on a roughly straight line in order to define the freedom + direction reasonnably... What I am tempted to demand here is a Dual Freedom Vector that much like + the Dual Projection Vector uses the coordinates of the original outline, rather than the grid- + fitted one. Hm... + + [parent1] + * + * + * + * + * + [child]**********[neighbour] maybe this situation + + + [child]**********[neighbour] or this one + * + * + * + * + * + [parent0] + + *****/ + + for (cont = 0; this->glyph->endPoint[cont] < child; cont++); + base = this->glyph->startPoint[cont]; n = this->glyph->endPoint[cont] - base + 1; inc = 1; dec = n - 1; + + pred = Next(child,base,n,dec); + if (!immediate && DistV(this->V[child],this->V[pred]) < this->emHeight/100) pred = Next(pred,base,n,dec); + predD = RDirectionV(this->V[pred],this->V[child]); + if (!immediate) { // try to get a "bigger" picture + temp = Next(pred,base,n,dec); tempD = RDirectionV(this->V[temp],this->V[child]); + while (DistV(this->V[child],this->V[pred]) < this->emHeight/12 && predD.x*tempD.x + predD.y*tempD.y >= this->cosF6) { + pred = temp; predD = tempD; + temp = Next(pred,base,n,dec); tempD = RDirectionV(this->V[temp],this->V[child]); + } + } + + succ = Next(child,base,n,inc); + if (!immediate && DistV(this->V[child],this->V[succ]) < this->emHeight/100) succ = Next(succ,base,n,inc); + succD = RDirectionV(this->V[succ],this->V[child]); + if (!immediate) { // try to get a "bigger" picture + temp = Next(succ,base,n,inc); tempD = RDirectionV(this->V[temp],this->V[child]); + while (DistV(this->V[child],this->V[succ]) < this->emHeight/12 && succD.x*tempD.x + succD.y*tempD.y >= this->cosF6) { + succ = temp; succD = tempD; + temp = Next(succ,base,n,inc); tempD = RDirectionV(this->V[temp],this->V[child]); + } + } + + parentD = RDirectionV(this->V[parent1],this->V[parent0]); + predP = VectProdRV(predD,parentD); + succP = VectProdRV(succD,parentD); + return Abs(predP) > Abs(succP) ? pred : succ; +} // TTSourceGenerator::Neighbour + +void TTSourceGenerator::AssertVectorsAtPotentialJunction(TTVector pv, short parent0, short parent1, short child) { // PV perpendicular to line to which to align + // pv is either the real pv or the dual pv + short deltaAngle,neighbour; + + this->tt->AssertTTVonLine(pv,parent0,parent1,this->V[parent0],this->V[parent1],true); + deltaAngle = this->attrib[child].deltaAngle; + if (-noJunction <= deltaAngle && deltaAngle <= +noJunction) { // we make a turn within ±noJunction° => align "perpendicularly" to "parent line" + // for CompilerLogic(1) this used to choose among x-axis or y-axis, whichever is closer to the parent line... + this->tt->AssertFVonPV(); + } else { // assume it is a junction of some kind, align "parallel" to joining line + neighbour = this->Neighbour(parent0,parent1,child,false); + this->tt->AssertTTVonLine(fv,child,neighbour,this->V[child],this->V[neighbour],false); + } +} /* TTSourceGenerator::AssertVectorsAtPotentialJunction */ + +FVMTDirection TTSourceGenerator::CalcDiagonalFVMT(FVOverride fv, short parent0, short parent1, short child, RVector strokeDirection, short *refPoint0, short *refPoint1) { + Attribute *attrib; + ListElem *diag = nullptr,*align = nullptr; + DiagParam *diagParam = nullptr; + AlignParam *alignParam = nullptr; + bool previousStroke,previousAlign; + short neighbour = this->Neighbour(parent0,parent1,child,true); + // short deltaAngle = this->attrib[child].deltaAngle; + short diagChild = 0,alignChild,otherChild; + Vector D = SubV(this->V[neighbour],this->V[child]); + FVMTDirection fvmt; + + attrib = &this->attrib[child]; + + for (diag = this->diagonals->first, previousStroke = false; diag && !previousStroke; diag = diag->next) { + diagParam = (DiagParam *)diag; + for (diagChild = 0; diagChild < 4 && diagParam->knot[diagChild] != child; diagChild++); + previousStroke = diagChild < 4; + } + for (align = this->aligns->first, previousAlign = false; align && !previousAlign; align = align->next) { + alignParam = (AlignParam *)align; + for (alignChild = 0; alignChild < alignParam->children && alignParam->child[alignChild] != child; alignChild++); + previousAlign = alignChild < alignParam->children; + } + + if (this->xSmooth && this->ySmooth) { // follow rule set #2 + + if (ScalProdRV(this->slope,strokeDirection) >= this->cosMT) { // rule #1: diagonal is within ±5° of main stroke angle + fvmt = fvOnX; + } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #2: neighbour within ±5° of x-axis + fvmt = fvOnX; + } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #3: neighbour within ±5° of y-axis + fvmt = fvOnY; + } else { // rule #4: kitchen sink case + fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = neighbour; + } + + } else { // follow rule set #1 + // for the first two cases, make a more or less logical decision + if (previousStroke) { // rule #1: child has already been involved in a [diagonal] stroke (more than two strokes would be difficult...) + otherChild = diagParam->knot[(diagChild + 2)%4]; + fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = otherChild; + } else if (previousAlign) { // rule #1B: child has already been involved in an align + // such as when doing the lower leg of an Arial K after aligning the junction points with the lower edge of the upper leg + fvmt = fvOnLine; *refPoint0 = alignParam->parent0; *refPoint1 = alignParam->parent1; + // for all the following cases, make an educated guess + } else if (ScalProdRV(this->slope,strokeDirection) >= this->cosMT) { // rule #2: diagonal is within ±5° of main stroke angle + fvmt = fvOnX; + } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #3: neighbour within ±5° of x-axis + fvmt = fvOnX; // fv = x + } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #4: neighbour within ±5° of y-axis + fvmt = fvOnY; // fv = y + } else if (!attrib->on && D.x == 0 && this->attrib[neighbour].touched[xRomanDir]) { // rule #5: preserve tangent property of x-extremum + fvmt = fvOnY; // dx == 0 => fv can only be y + } else if (!attrib->on && D.y == 0 && this->attrib[neighbour].touched[yRomanDir]) { // rule #6: preserve tangent property of y-extremum + fvmt = fvOnX; // dy == 0 => fv can only be x + } else if (this->xSmooth || this->ySmooth) { // rules #7,8: all x or y are fixed, can only move in y or x respectively + fvmt = this->xSmooth ? fvOnY : fvOnX; + // } else if (Abs(strokeDirection.x) != Abs(strokeDirection.y)) { // rules #9,10: kitchen sink case (all other rules haven't applied), diagonal is within ±45° of x or y-axis + } else { + fvmt = Abs(strokeDirection.x) > Abs(strokeDirection.y) ? fvOnY : fvOnX; + // } else { // rule #11: 45°-case of kitchen sink case... + // fvmt = fvOnPV; + } + } + return fvmt; +} // TTSourceGenerator::CalcDiagonalFVMT + +FVMTDirection TTSourceGenerator::CalcAlignFVMT(FVOverride fv, short parent0, short parent1, short child, RVector alignDirection, short *refPoint0, short *refPoint1) { + Attribute *attrib = &this->attrib[child]; + ListElem *diag = nullptr,*align = nullptr; + DiagParam *diagParam = nullptr; + AlignParam *alignParam = nullptr; + bool previousStroke,previousAlign; + short neighbour = this->Neighbour(parent0,parent1,child,true); + short deltaAngle = attrib->deltaAngle; + short diagChild = 0,alignChild,otherChild; + Vector D = SubV(this->V[neighbour],this->V[child]); + FVMTDirection fvmt; + + for (diag = this->diagonals->first, previousStroke = false; diag && !previousStroke; diag = diag->next) { + diagParam = (DiagParam *)diag; + for (diagChild = 0; diagChild < 4 && diagParam->knot[diagChild] != child; diagChild++); + previousStroke = diagChild < 4; + } + for (align = this->aligns->first, previousAlign = false; align && !previousAlign; align = align->next) { + alignParam = (AlignParam *)align; + for (alignChild = 0; alignChild < alignParam->children && alignParam->child[alignChild] != child; alignChild++); + previousAlign = alignChild < alignParam->children; + } + + if (this->xSmooth && this->ySmooth) { // follow rule set #2 + if (ScalProdRV(this->slope,alignDirection) >= this->cosMT) { // rule #1: diagonal is within ±5° of main stroke angle + fvmt = fvOnX; + } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #2: neighbour within ±5° of x-axis + fvmt = fvOnX; + } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #3: neighbour within ±5° of y-axis + fvmt = fvOnY; + } else if (-noJunction <= deltaAngle && deltaAngle <= +noJunction) { // rule #4a: don't put fv on line to neighbor... + fvmt = fvOnPV; + } else { // ...unless assumed to be a junction + fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = neighbour; + } + } else { // follow modified rule set #1 + // for the first four cases, make a more or less logical decision + if (previousStroke) { // rule #1: child has already been involved in a [diagonal] stroke (more than two strokes would be difficult...) + otherChild = diagParam->knot[(diagChild + 2)%4]; + fvmt = fvOnLine; *refPoint0 = child; *refPoint1 = otherChild; + } else if (previousAlign) { // rule #1B: child has already been involved in an align + fvmt = fvOnLine; *refPoint0 = alignParam->parent0; *refPoint1 = alignParam->parent1; + } else if (fv == fvForceX) { + fvmt = fvOnX; + } else if (fv == fvForceY) { + fvmt = fvOnY; + // for all the following cases, make an educated guess + } else if (attrib->touched[xRomanDir] != attrib->touched[yRomanDir]) { // x xor y are fixed, can only move in y or x respectively + fvmt = attrib->touched[xRomanDir] ? fvOnY : fvOnX; + } else if (this->xSmooth != this->ySmooth) { // rules #7,8: all x or y are fixed, can only move in y or x respectively + fvmt = this->xSmooth ? fvOnY : fvOnX; + } else if (ScalProdRV(this->slope,alignDirection) >= this->cosMT) { // rule #2: diagonal is within ±5° of main stroke angle + fvmt = fvOnX; + } else if (attrib->on && Abs(D.y) <= Abs(D.x)*this->tanMT) { // rule #3: neighbour within ±5° of x-axis + fvmt = fvOnX; // fv = x + } else if (attrib->on && Abs(D.x) <= Abs(D.y)*this->tanMT) { // rule #4: neighbour within ±5° of y-axis + fvmt = fvOnY; // fv = y +// } else if (!attrib->on && D.x == 0 && this->attrib[neighbour].touched[xRomanDir]) { // rule #5: preserve tangent property of x-extremum +// fvmt = fvOnY; // dx == 0 => fv can only be y +// } else if (!attrib->on && D.y == 0 && this->attrib[neighbour].touched[yRomanDir]) { // rule #6: preserve tangent property of y-extremum +// fvmt = fvOnX; // dy == 0 => fv can only be x +// } else if (Abs(alignDirection.x) != Abs(alignDirection.y)) { // rules #9,10: kitchen sink case (all other rules haven't applied), diagonal is within ±45° of x or y-axis +// } else { +// fvmt = Abs(alignDirection.x) > Abs(alignDirection.y) ? fvOnY : fvOnX; + } else { // rule #11: 45°-case of kitchen sink case... + fvmt = fvOnPV; + } + } + return fvmt; +} // TTSourceGenerator::CalcAlignFVMT + +void TTSourceGenerator::AssertFVMT(FVMTDirection fvmt, short point0, short point1) { + switch (fvmt) { + case fvOnX: this->tt->AssertFVonCA(false); break; + case fvOnY: this->tt->AssertFVonCA(true); break; + case fvOnPV: this->tt->AssertFVonPV(); break; + case fvOnLine: this->tt->AssertTTVonLine(fv,point0,point1,this->V[point0],this->V[point1],false); break; + } +} // TTSourceGenerator::AssertFVMT + +void TTSourceGenerator::Touched(short knot, TTVDirection dir) { + bool *touched = &this->attrib[knot].touched[0]; +// short i; + +// xRomanDir = 0, yRomanDir, xItalDir, yItalDir, xAdjItalDir, yAdjItalDir, diagDir, perpDiagDir +// for italic angles in x, pv is perpendicular to the italic angle, while fv is x, hence touches x only +// for italic angles in y, pv is y, while fv is parallel to the italic angle, hence touches x and y + touched[xRomanDir] = touched[xRomanDir] || dir == xRomanDir || dir >= xItalDir; + touched[yRomanDir] = touched[yRomanDir] || dir == yRomanDir || dir == yItalDir || dir == yAdjItalDir || dir >= diagDir; +// for (i = xRomanDir; i < xItalDir; i++) touched[i] |= dir == i || dir >= xItalDir; +} /* TTSourceGenerator::Touched */ + +short TTSourceGenerator::TheCvt(short parent, short child, LinkColor color, LinkDirection direction, CvtCategory category, short distance) { + Vector D; + + if (color < 0) color = this->glyph->TheColor(parent,child); // provide default color + if (category < 0) category = cvtDistance; // provide default category + if (distance < 0) { // provide default distance + D = SubV(this->V[parent],this->V[child]); + distance = direction == linkY ? (short)D.x : (short)D.y; + distance = Abs(distance); + } + return (short)this->font->TheCvt()->GetBestCvtMatch(this->charGroup,color,direction,category,distance); +} /* TTSourceGenerator::TheCvt */ + +/***** The VacuFormRound code still contains some Sampo-like convolutions... + But before attempting to improve this, it would be worthwhile re-thinking the concept + of VacuFormRound in general: what do we want it to achieve, and how do we achieve this. + Hopefully, this reduces the many different functions necessary to implement VacuForm- + Round, and therefore makes the overall code simpler, not needing that many decisions, + which function is to be called depending on what VacuFormRound type and configuration + of knots... For the curious: cf. fdefs0.c *****/ +void TTSourceGenerator::DoVacuFormRound(void) { + short pass,labelNumber,labels = 0,i,j,first,last,lo,hi,l,h,type,x1,x2,y1,y2,nextX1,nextX2,nextY1,nextY2,base1,n1,inc1,dec1,base2,n2,inc2,dec2,fun,knots,knot[maxVacuForms],next,dist; + VacuFormParams *vacu; + wchar_t buf[32]; + + for (first = 0; first < this->vacuForms && this->vacuForm[first].type == 2; first++); // type 2 vacuforms need a guard for each instance, since the guard measures a distance, + for (last = this->vacuForms-1; last >= 0 && this->vacuForm[last].type == 2; last--); // while all other types get by on one single guard which depends on a ppem size. + + if (this->vacuFormLimit >= 0) { + labels = 1; + for (i = 0; i < this->vacuForms; i++) if (this->vacuForm[i].type == 2) labels++; + this->tt->Emit(L"MPPEM[]"); + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"GT[], %hi, *",this->vacuFormLimit); this->tt->Emit(buf); + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"JROF[], #LRnd%hi, *",labels); this->tt->Emit(buf); // branch if vacuFormLimit > ppem... + this->tt->Emit(L"#BEGIN"); + } + labelNumber = 0; + for (pass = 0; pass <= 3; pass++) { + + for (i = 0; i < this->vacuForms; i++) { + + vacu = &this->vacuForm[i]; + type = vacu->type; + + if ((pass < 3) == (type != 2)) { // in passes 0 through 2 do all types != 2, and in pass 3 do type 2 + + x1 = vacu->knot[0]; this->attrib[x1].vacu = true; // start points may be off-curve points + x2 = vacu->knot[1]; this->attrib[x2].vacu = true; // hence include them into the candidates + y1 = vacu->knot[2]; this->attrib[y1].vacu = true; // for points to be flipped to on-curve + y2 = vacu->knot[3]; this->attrib[y2].vacu = true; // upon completing passes 2 and 3... + + if ((pass == 0 && i == first) || pass == 3) { + if (pass == 3) { // type == 2 + this->tt->AssertFreeProjVector(yRomanDir); + if (this->glyph->y[y2] > this->glyph->y[y1]) swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"MD[N], %hi, %hi",y2,y1); + else swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"MD[N], %hi, %hi",y1,y2); + this->tt->Emit(buf); + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"EQ[], 64, *"); this->tt->Emit(buf); + } else { + swprintf(buf,sizeof(buf)/sizeof(wchar_t), L"RS[], %hi",MAIN_STROKE_IS_ONE_PIXEL_STORAGE); this->tt->Emit(buf); + } + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"JROF[], #LRnd%hi, *",labelNumber); this->tt->Emit(buf); + this->tt->Emit(L"#BEGIN"); + } + + for (j = 0; x1 > this->glyph->endPoint[j]; j++); + base1 = this->glyph->startPoint[j]; n1 = this->glyph->endPoint[j] - base1 + 1; + if (vacu->forward[0]) { inc1 = 1; dec1 = n1 - 1; } else { inc1 = n1 - 1; dec1 = 1; } + + for (j = 0; x2 > this->glyph->endPoint[j]; j++); + base2 = this->glyph->startPoint[j]; n2 = this->glyph->endPoint[j] - base2 + 1; + if (vacu->forward[1]) { inc2 = 1; dec2 = n2 - 1; } else { inc2 = n2 - 1; dec2 = 1; } + + nextX1 = Next(x1,base1,n1,inc1); this->attrib[nextX1].vacu = true; + nextX2 = Next(x2,base2,n2,inc2); this->attrib[nextX2].vacu = true; + nextY1 = Next(y1,base1,n1,dec1); this->attrib[nextY1].vacu = true; + nextY2 = Next(y2,base2,n2,dec2); this->attrib[nextY2].vacu = true; + + if (pass == 1 || pass == 3) { // move horizontal tangent points to make tangent comply with radius (defined by cvt) + this->tt->AssertFreeProjVector(xRomanDir); + dist = this->glyph->x[x1]-this->glyph->x[nextX1]; + if (Abs(dist) > vacuFudge(this->font)) { + this->tt->AssertRefPoint(0,x1); + this->tt->ALIGNRP(nextX1); + } + dist = this->glyph->x[x2]-this->glyph->x[nextX2]; + if (Abs(dist) > vacuFudge(this->font)) { + this->tt->AssertRefPoint(0,x2); + this->tt->ALIGNRP(nextX2); + } + if (this->glyph->x[x1] < this->glyph->x[y1]) { // with the axis + fun = type == 3 ? 2 : 0; + this->tt->CALL012345(fun,nextY2,nextY1,x2,vacu->cvt); + } else { // against the axis + fun = type == 3 ? 3 : 1; + if (type == 2) this->tt->CALL012345(fun,nextY2,nextY1,x2,vacu->cvt); + else this->tt->CALL012345(fun,nextY1,nextY2,x1,vacu->cvt); + } + } + + if (pass == 0 || pass == 3) { // move vertical tangent points to make tangent comply with radius (defined by cvt) + this->tt->AssertFreeProjVector(yRomanDir); + dist = this->glyph->y[y1]-this->glyph->y[nextY1]; + if (Abs(dist) > vacuFudge(this->font)) { + this->tt->AssertRefPoint(0,y1); + this->tt->ALIGNRP(nextY1); + } + dist = this->glyph->y[y2]-this->glyph->y[nextY2]; + if (Abs(dist) > vacuFudge(this->font)) { + this->tt->AssertRefPoint(0,y2); + this->tt->ALIGNRP(nextY2); + } + if (type == 2) { + fun = this->glyph->y[x1] > this->glyph->y[y1] ? 4 : 5; // with the axis and against the axis + this->tt->CALL012345(fun,nextX2,nextX1,y2,vacu->cvt); + } else { + if (type == 4) { + fun = this->glyph->y[x1] > this->glyph->y[y1] ? 2 : 3; // with the axis and against the axis + } else { // type == 1 + fun = this->glyph->y[x1] > this->glyph->y[y1] ? 0 : 1; // with the axis and against the axis + } + if (this->glyph->x[x1] < this->glyph->x[y1]) this->tt->CALL012345(fun,nextX2,nextX1,y2,vacu->cvt); + else this->tt->CALL012345(fun,nextX1,nextX2,y1,vacu->cvt); + } + } + + if (pass == 2 || pass == 3) { // move remaining points "diagonally" to (vertical) tangent points + if (nextX1 != nextY1 && nextX1 != y1 && nextY1 != x1) { // inner contour (or outer) + knots = 0; next = Next(nextX1,base1,n1,inc1); + while (knots < maxVacuForms && next != nextY1) { + knot[knots] = next; knots++; + next = Next(next,base1,n1,inc1); + } + if (knots > 1) + this->tt->CALL6(knots,knot,nextX1); + else if (knots == 1) { + if (nextX1 == knot[0] + 1) this->tt->CALL378(37,knot[0]); // special case of function 6 for nextX1 = knot[0] + 1... + else if (nextX1 == knot[0] - 1) this->tt->CALL378(38,knot[0]); // special case of function 6 for nextX1 = knot[0] - 1... + else this->tt->CALL6(knots,knot,nextX1); + } + for (j = 0; j < knots; j++) this->attrib[knot[j]].vacu = true; + } + if (nextX2 != nextY2 && nextX2 != y2 && nextY2 != x2) { // outer contour (or inner) + knots = 0; next = Next(nextX2,base2,n2,inc2); + while (knots < maxVacuForms && next != nextY2) { + knot[knots] = next; knots++; + next = Next(next,base2,n2,inc2); + } + if (knots > 1) + this->tt->CALL6(knots,knot,nextX2); + else if (knots == 1) { + if (nextX2 == knot[0] + 1) this->tt->CALL378(37,knot[0]); // special case of function 6 for nextX2 = knot[0] + 1... + else if (nextX2 == knot[0] - 1) this->tt->CALL378(38,knot[0]); // special case of function 6 for nextX2 = knot[0] - 1... + else this->tt->CALL6(knots,knot,nextX2); + } + for (j = 0; j < knots; j++) this->attrib[knot[j]].vacu = true; + } + } + + if ((pass == 2 && i == last) || pass == 3) { + lo = 0; + while (lo < this->knots) { // determine a range of (off-curve) points that have been moved "diagonally" (vacu flag == true) + while (lo < this->knots && !(this->attrib[lo].vacu || this->glyph->onCurve[lo])) lo++; + hi = lo; + // optimise for code size: include all points which are vacu or on-curve + while (hi+1 < this->knots && (this->attrib[hi+1].vacu || this->glyph->onCurve[hi+1])) hi++; + // optimise for speed: exclude any on-curve points on the fringe of the interval + for (l = lo; l <= hi && this->glyph->onCurve[l]; l++); + for (h = hi; h >= lo && this->glyph->onCurve[h]; h--); + if (h > l) swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"FLIPRGON[], %hi, %hi",l,h); else if (h == l) swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"FLIPPT[], %hi",l); + if (h >= l) this->tt->Emit(buf); + lo = hi+1; + } + this->tt->Emit(L"#END"); + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"#LRnd%hi:",labelNumber); this->tt->Emit(buf); + labelNumber++; + for (j = 0; j < this->knots; j++) this->attrib[j].vacu = false; // reset + } + } + } + } + if (this->vacuFormLimit >= 0) { + this->tt->Emit(L"#END"); + swprintf(buf,sizeof(buf)/sizeof(wchar_t),L"#LRnd%hi:",labels); this->tt->Emit(buf); + } + + this->vacuFormLimit = -1; + this->vacuForms = 0; +} /* TTSourceGenerator::DoVacuFormRound */ + +TTGenerator *NewTTSourceGenerator(void) { + return new TTSourceGenerator; +} + +/* Notice that FixDStrokes touches all untouched points of strokes that happen to be diagonal or italic + (but not the DStroke or IStroke), but the only point that might stay untouched upon a stroke is the + "upper left" (i.e. B1 in the stroke's sketch), which typically gets touched anyhow as a result of + SetItalicStrokePhase/Angle, and which is a stationary point (because presumably it has a link to it), + we could simply MDAP[r] it if SetItalicStrokePhase/Angle didn't touch it, and forget about keeping + track of the touched flags... Put another way, most calls to Touched() are redundant, since they mark + a point that was never involved in a non-horizontal and non-vertical STROKE. In any case it looks as + if FixDStrokes, if it remains in use, could be an informative command. Another thing I could do is + to move the Touched() update into the TTEngine, and in order for FixDStrokes to be able to work with + the data hidden, provide for an inquiry function bool Touched(bool y). This could then get us + rid of the FVDir() inquiry function... + + 2007-04-19 Not anymore. In order to implement XSmooth/() we need to know which points have been touched, + and if so, in x or y (or both). Furthermore, the more recent Res* commands encapsulate a plurality of + TT instructions, hence updating a flag inside TTEngine does not make the task of tracking this any easier. + + Question: Does SetItalicStrokePhase/Angle really do what we want it to? Wouldn't we rather want to + move the points down to the base line, then xlink/xinterpolate them (at which point they get rounded), + and finally move them back up, along the (meanwhile) adjusted angle? Notice that this is what SetItalc- + StrokePhase/Angle never did, but chances are, what it was intended to. + + In the future might rewrite also c1_BestClusterIndex and see, whether all of this makes sense the way + it is. + + Plus, should assert that the hinter parameters are up-to-date (compiled or so) prior to code generation. + + Should also assert that eg. VacuFormRound doesn't attempt to 'do' the side-bearing points. This becomes + the longer the more an issue which maybe should be more centralised, such as in the parser. The problem + is that there are things about which only the code generator knows, which means the particular code + generator at issue, such as implementation restrictions (how many links from a point etc.), so the parser + would have to know more about that particular code generator than I like. On the other hand there are + checks that apply to all code generators, and preferreably are done once and forever (ie. in the parser), + such as not to try to VacuFormRound the side bearing points, etc. + + */ diff --git a/src/TTGenerator.h b/src/TTGenerator.h index b158f1d..75a10ff 100644 --- a/src/TTGenerator.h +++ b/src/TTGenerator.h @@ -1,73 +1,73 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/***** - - TTGenerator.h - New TypeMan Talk Compiler - Code Generators - -*****/ - -#ifndef TTGenerator_dot_h -#define TTGenerator_dot_h - -#define STRAIGHTANGLEFUDGE 1.5 /* degrees (7.5->5.0->1.5) */ - -typedef struct { - TTVectorDesc pv; - TTVectorDesc fv[maxParams]; -// TTVDirection pv; // if pvKnot1 != invalidKnotNum, SDPVTL[r] (!perpPV) or SDPVTL[R] (perpPV) -// short pvKnot0,pvKnot1; // if pvKnot1 != invalidKnotNum, SDPVTL[?], pvKnot0, pvKnot1 -// TTVDirection fv[maxParams]; // if fvKnot1[i] != invalidKnotNum, SFVTL[r] (!perpFV) or SFVTL[R] (perpFV) (for each of the params of Interpolate) -// short fvKnot0[maxParams],fvKnot1[maxParams]; // if fvKnot1[i] != invalidKnotNum, SFVTL[?], fvKnot0, fvKnot1 (for each of the params of Interpolate) -} ProjFreeVector; - -class TTGenerator { -public: - virtual void MainStrokeAngle(short angle100, wchar_t error[]); - virtual void GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[]); - virtual void SetRounding(bool y, Rounding round, short params, short param[]); - virtual void SetItalicStroke(bool phase, wchar_t error[]); /* !phase => angle */ - virtual void Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[]); - virtual void GrabHereInX(short left, short right, wchar_t error[]); - // bool y kept around mostly for the GUI---display both links even though one or both may be diagDir or perpDiagDir - virtual void Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[]); - virtual void Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[]); - virtual void BeginCodePath(short fpgmBias, wchar_t error[]); - virtual void EndCodePath(wchar_t error[]); - virtual void ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[]); - virtual void ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[]); - virtual void ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[]); - virtual void ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[]); - virtual void ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[]); - virtual void ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]); - virtual void ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]); - virtual void ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[]); - virtual void Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[]); - virtual void Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[]); - virtual void Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[]); - virtual void Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[]); - virtual void Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[]); - virtual void DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[]); // new diagonal stroke - virtual void IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[]); // new italic stroke - virtual void FixDStrokes(void); - virtual void Serif(bool forward, short type, short knots, short knot[], wchar_t error[]); - virtual void Scoop(short parent0, short child, short parent1, wchar_t error[]); - virtual void Smooth(short y, short italicFlag); - virtual void Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[]); - virtual void VacuFormLimit(short ppem); - virtual void VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[]); - virtual void Call(short actParams, short anyNum[], short functNum); - virtual void Asm(bool inLine, wchar_t text[], wchar_t error[]); - virtual void Quit(void); - virtual void InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError); - virtual void TermTTGenerator(void); - TTGenerator(void); - virtual ~TTGenerator(void); -}; - -bool ClassifyAlign(Vector parent0, Vector child, Vector parent1, short ppem); -bool ClassifyStroke(Vector A1, Vector A2, Vector B1, Vector B2, short ppem, bool *crissCross, RVector *strokeDirection, bool *xLinks, short distance[], wchar_t error[]); - -TTGenerator *NewTTSourceGenerator(void); - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/***** + + TTGenerator.h - New TypeMan Talk Compiler - Code Generators + +*****/ + +#ifndef TTGenerator_dot_h +#define TTGenerator_dot_h + +#define STRAIGHTANGLEFUDGE 1.5 /* degrees (7.5->5.0->1.5) */ + +typedef struct { + TTVectorDesc pv; + TTVectorDesc fv[maxParams]; +// TTVDirection pv; // if pvKnot1 != invalidKnotNum, SDPVTL[r] (!perpPV) or SDPVTL[R] (perpPV) +// short pvKnot0,pvKnot1; // if pvKnot1 != invalidKnotNum, SDPVTL[?], pvKnot0, pvKnot1 +// TTVDirection fv[maxParams]; // if fvKnot1[i] != invalidKnotNum, SFVTL[r] (!perpFV) or SFVTL[R] (perpFV) (for each of the params of Interpolate) +// short fvKnot0[maxParams],fvKnot1[maxParams]; // if fvKnot1[i] != invalidKnotNum, SFVTL[?], fvKnot0, fvKnot1 (for each of the params of Interpolate) +} ProjFreeVector; + +class TTGenerator { +public: + virtual void MainStrokeAngle(short angle100, wchar_t error[], size_t errorLen); + virtual void GlyphStrokeAngle(short riseCvt, short runCvt, wchar_t error[], size_t errorLen); + virtual void SetRounding(bool y, Rounding round, short params, short param[]); + virtual void SetItalicStroke(bool phase, wchar_t error[], size_t errorLen); /* !phase => angle */ + virtual void Anchor(bool y, ProjFreeVector *projFreeVector, short knot, short cvt, bool round, wchar_t error[], size_t errorLen); + virtual void GrabHereInX(short left, short right, wchar_t error[], size_t errorLen); + // bool y kept around mostly for the GUI---display both links even though one or both may be diagDir or perpDiagDir + virtual void Link(bool y, bool dist, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent, short child, CvtCategory category, short cvt, short minDists, short jumpPpemSize[], F26Dot6 pixelSize[], short *actualCvt, wchar_t error[], size_t errorLen); + virtual void Interpolate(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short children, short child[], short parent1, bool round, wchar_t error[], size_t errorLen); + virtual void BeginCodePath(short fpgmBias, wchar_t error[], size_t errorLen); + virtual void EndCodePath(wchar_t error[], size_t errorLen); + virtual void ResAnchor(bool y, ProjFreeVector *projFreeVector, short child, short cvt, wchar_t error[], size_t errorLen); + virtual void ResIPAnchor(bool y, ProjFreeVector *projFreeVector, bool postRoundFlag, short parent0, short child, short parent1, wchar_t error[], size_t errorLen); + virtual void ResLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent, short child, short cvt, short minDists, wchar_t error[], size_t errorLen); + virtual void ResIPLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent, short child, short cvt, short grandParent1, wchar_t error[], size_t errorLen); + virtual void ResDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, wchar_t error[], size_t errorLen); + virtual void ResIPDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen); + virtual void ResIPDDLink(bool y, bool dist, ProjFreeVector *projFreeVector, short strokeOptimizationFlag, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen); + virtual void ResIIPDLink(bool dist, ProjFreeVector *projFreeVector, short grandParent0, short parent0, short child0, short cvt0, short parent1, short child1, short cvt1, short grandParent1, wchar_t error[], size_t errorLen); + virtual void Intersect(short intersection, short line0start, short line0end, short line1start, short line1end, short ppem0, short ppem1, wchar_t error[], size_t errorLen); + virtual void Align(FVOverride fvOverride, short parent0, short children, short child[], short parent1, short ppem, wchar_t error[], size_t errorLen); + virtual void Move(bool y, F26Dot6 amount, short knots, short knot[], wchar_t errMsg[], size_t errMsgLen); + virtual void Shift(bool y, ProjFreeVector *projFreeVector, short parent, short children, short child[], wchar_t error[], size_t errorLen); + virtual void Stroke(FVOverride fvOverride, bool leftStationary[], short knot[], short cvt, short ppem, short *actualCvt, wchar_t error[], size_t errorLen); + virtual void DStroke(bool leftStationary[], short knot[], short cvt, short *actualCvt, wchar_t error[], size_t errorLen); // new diagonal stroke + virtual void IStroke(bool leftStationary[], short knot[], short height[], short phase, short cvt, short *actualCvt, wchar_t error[], size_t errorLen); // new italic stroke + virtual void FixDStrokes(void); + virtual void Serif(bool forward, short type, short knots, short knot[], wchar_t error[], size_t errorLen); + virtual void Scoop(short parent0, short child, short parent1, wchar_t error[], size_t errorLen); + virtual void Smooth(short y, short italicFlag); + virtual void Delta(bool y, DeltaColor color, short knot, F26Dot6 amount, bool ppemSize[], wchar_t errMsg[], size_t errMsgLen); + virtual void VacuFormLimit(short ppem); + virtual void VacuFormRound(short type, short radius, bool forward[], short knot[], wchar_t error[], size_t errorLen); + virtual void Call(short actParams, short anyNum[], short functNum); + virtual void Asm(bool inLine, wchar_t text[], wchar_t error[], size_t errorLen); + virtual void Quit(void); + virtual void InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph, int32_t glyphIndex, TTEngine *tt, bool legacyCompile, bool *memError); + virtual void TermTTGenerator(void); + TTGenerator(void); + virtual ~TTGenerator(void); +}; + +bool ClassifyAlign(Vector parent0, Vector child, Vector parent1, short ppem); +bool ClassifyStroke(Vector A1, Vector A2, Vector B1, Vector B2, short ppem, bool *crissCross, RVector *strokeDirection, bool *xLinks, short distance[], wchar_t error[], size_t errorLen); + +TTGenerator *NewTTSourceGenerator(void); + #endif // TTGenerator_dot_h \ No newline at end of file diff --git a/src/TextBuffer.cpp b/src/TextBuffer.cpp index d00929c..9b08842 100644 --- a/src/TextBuffer.cpp +++ b/src/TextBuffer.cpp @@ -1,361 +1,360 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS -#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING - -#include // assert -#include // swprintf -#include // strncpy etc. -#include -#include -#include -#include -#include -#include -#include - -#include "TextBuffer.h" -#ifndef _WIN32 -#define swprintf(wcs, ...) swprintf(wcs, 1024, __VA_ARGS__) -#endif - -#ifndef Min - #define Min(a,b) ((a) < (b) ? (a) : (b)) -#endif -#ifndef Max - #define Max(a,b) ((a) > (b) ? (a) : (b)) -#endif - -TextBuffer::TextBuffer(void) { - this->size = this->used = 0; - this->indent = 0; - this->modified = false; - this->text = NULL; -} // TextBuffer::TextBuffer - -TextBuffer::~TextBuffer(void) { - if (this->text != NULL) free(this->text); -} // TextBuffer::~TextBuffer - -void TextBuffer::Indent(int32_t indent) { - this->indent = Max(0,this->indent + indent); -} // TextBuffer::Indent - -bool TextBuffer::GetModified(void) { - return this->modified; -} // TextBuffer::GetModified - -void TextBuffer::SetModified(bool on) { - this->modified = on; -} // TextBuffer::SetModified - -size_t TextBuffer::Length(void) { - return this->used; -} // TextBuffer::Length - -int32_t TextBuffer::TheLength() -{ - return (int32_t)this->Length(); -} - -int32_t TextBuffer::TheLengthInUTF8(void) -{ - std::string text8; - - std::wstring_convert> converter; - text8 = converter.to_bytes(this->text); - - return static_cast(text8.length()); -} - -int32_t TextBuffer::LineNumOf(int32_t pos) { - wchar_t *text,*textEnd; - int32_t lineNum; - - for (text = this->text, textEnd = text + Max(0,Min(pos,(int32_t)this->used)), lineNum = 0; text < textEnd; text++) - if (*text == textBufferLineBreak) - lineNum++; - - return lineNum; -} // TextBuffer::LineNumOf - -int32_t TextBuffer::PosOf(int32_t lineNum) { - wchar_t *text,*textEnd; - - for (text = this->text, textEnd = text + this->used; lineNum > 0 && text < textEnd; text++) - if (*text == textBufferLineBreak) - lineNum--; - - return (int32_t)(ptrdiff_t)(text - this->text); -} // TextBuffer::PosOf - - -void TextBuffer::GetText(size_t *textLen, char text[]) -{ - std::string text8; - - std::wstring_convert> converter; - text8 = converter.to_bytes(this->text); - - *textLen = static_cast(text8.size()); - if (*textLen > 0) - { - std::copy(text8.begin(), text8.end(), text); - text[text8.size()] = '\0'; // don't forget the terminating 0 - } -} - -void TextBuffer::GetText(size_t *textLen, wchar_t text[]) { - *textLen = this->used; - if (*textLen > 0) wcsncpy(text,this->text,*textLen); - text[*textLen] = L'\0'; -} // TextBuffer::GetText - -void TextBuffer::GetText(std::wstring &text) -{ - text.assign(this->text); -} - -void TextBuffer::SetText(int32_t textLen, const char text[]) -{ - if (textLen == 0) - { - this->SetText(0, L""); - } - else - { - std::string str(reinterpret_cast(text), textLen); - std::wstring_convert> converter; - std::wstring wstr = converter.from_bytes(str); - - this->SetText(wstr.length(), wstr.c_str()); - } -} - -void TextBuffer::SetText(size_t textLen, const wchar_t text[]) { - if (!this->AssertTextSize(textLen + 1 - this->used)) return; - - if (textLen > 0) - wcsncpy(this->text,text,textLen); - this->used = textLen; - this->text[this->used] = L'\0'; - this->modified = false; // !!! -} // TextBuffer::SetText - -wchar_t TextBuffer::GetCh(int32_t atPos) { - return 0 <= atPos && atPos < (int32_t)this->used ? this->text[atPos] : L'\0'; -} // TextBuffer::GetCh - -void TextBuffer::GetLine(int32_t atPos, int32_t *lineLen, wchar_t line[], int32_t *lineSepLen) { - int32_t startPos,endPos,usedLineLen; - - *lineLen = *lineSepLen = 0; - if (atPos < 0 || (int32_t)this->used <= atPos) return; - startPos = atPos; - while (startPos > 0 && this->text[startPos-1] != textBufferLineBreak) startPos--; - endPos = atPos; - while (endPos < (int32_t)this->used && this->text[endPos] != textBufferLineBreak) endPos++; - *lineLen = endPos - startPos; - *lineSepLen = 1; - usedLineLen = Min(*lineLen,0xff); - wcsncpy(line,&this->text[startPos],usedLineLen); - line[usedLineLen] = L'\0'; -} // TextBuffer::GetLine - -void TextBuffer::GetRange(int32_t fromPos, int32_t toPos, wchar_t text[]) { - int32_t chars; - - fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)this->used)); - toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)this->used)); - chars = toPos - fromPos; - if (chars < 0) return; - - if (chars > 0) wcsncpy(text,&this->text[fromPos],chars); - text[chars] = L'\0'; -} // TextBuffer::GetRange - -int32_t TextBuffer::Search(int32_t fromPos, bool wrapAround, wchar_t target[], bool caseSensitive) { -#ifdef _DEBUG - assert(false); -#endif - - return -1; // not found -} // TextBuffer::Search - -void TextBuffer::Append(const wchar_t strg[]) { - size_t chars; - - chars = wcslen(strg); - if (chars <= 0) return; - - if (!this->AssertTextSize(chars + 1)) return; - - if (chars > 0) wcsncpy(&this->text[this->used],strg,chars); - this->used += chars; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::Append - -void TextBuffer::AppendRange(const wchar_t strg[], int32_t fromPos, int32_t toPos) { - size_t chars; - - chars = wcslen(strg); - if (chars <= 0) return; - - fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)chars)); - toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)chars)); - chars = toPos - fromPos; - if (chars <= 0) return; - - if (!this->AssertTextSize(chars + 1)) return; - - wcsncpy(&this->text[this->used],&strg[fromPos],chars); - this->used += chars; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::AppendRange - -void TextBuffer::AppendCh(wchar_t ch) { - if (!this->AssertTextSize(1 + 1)) return; - - this->text[this->used++] = ch; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::AppendCh - -void TextBuffer::AppendLine(const wchar_t strg[]) { - size_t chars, charsEol; - int32_t indent; - wchar_t eol[4]; - - chars = wcslen(strg); - if (chars < 0) return; // hardly, though... - - indent = chars > 0 ? this->indent : 0; - - swprintf(eol,L"\r"); - charsEol = wcslen(eol); - - if (!this->AssertTextSize(indent + chars + charsEol + 1)) return; - - if (indent > 0) - memset(&this->text[this->used],L' ',indent * sizeof(wchar_t)); - this->used += indent; - if (chars > 0) - wcsncpy(&this->text[this->used],strg,chars); - this->used += chars; - wcsncpy(&this->text[this->used],eol,charsEol); - this->used += charsEol; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::AppendLine - -void TextBuffer::AppendText(const TextBuffer *text) { - if (text->used > 0) this->AppendRange(text->text,0,(int32_t)text->used); -} // TextBuffer::AppendText - -void TextBuffer::AppendTextRange(const TextBuffer *text, int32_t fromPos, int32_t toPos) { - fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)text->used)); - toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)text->used)); - if (fromPos < toPos) this->AppendRange(text->text,fromPos,toPos); -} // TextBuffer::AppendTextRange - -void TextBuffer::Insert(int32_t atPos, const wchar_t strg[]) { - size_t chars; - int32_t remainder; - - chars = wcslen(strg); - if (chars <= 0) return; - - if (!this->AssertTextSize(chars + 1)) return; - - atPos =(int32_t) Max(0,Min(atPos,(int32_t)this->used)); - - remainder = (int32_t)this->used - atPos; - if (remainder > 0) - memmove(&this->text[atPos + chars],&this->text[atPos],remainder * sizeof(wchar_t)); - - wcsncpy(&this->text[atPos],strg,chars); - this->used += chars; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::Insert - -void TextBuffer::InsertRange(int32_t atPos, const wchar_t strg[], int32_t fromPos, int32_t toPos) { - int32_t remainder; - - size_t chars = wcslen(strg); - if (chars <= 0) return; - - fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)chars)); - toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)chars)); - chars = toPos - fromPos; - if (chars <= 0) return; - - if (!this->AssertTextSize(chars + 1)) return; - - atPos = (int32_t)Max(0,Min(atPos,(int32_t)this->used)); - - remainder = (int32_t)this->used - atPos; - if (remainder > 0) - memmove(&this->text[atPos + chars],&this->text[atPos],remainder * sizeof(wchar_t)); - - wcsncpy(&this->text[atPos],&strg[fromPos],chars); - this->used += chars; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::InsertRange - -void TextBuffer::InsertCh(int32_t atPos, wchar_t ch) { - int32_t remainder; - - if (!this->AssertTextSize(1 + 1)) return; - - atPos = (int32_t)Max(0,Min(atPos,(int32_t)this->used)); - - remainder = (int32_t)this->used - atPos; - if (remainder > 0) - memmove(&this->text[atPos + 1],&this->text[atPos],remainder * sizeof(wchar_t)); - - this->text[atPos] = ch; - this->used++; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::InsertCh - -void TextBuffer::Delete(int32_t fromPos, int32_t toPos) { - int32_t chars,remainder; - - fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)this->used)); - toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)this->used)); - chars = toPos - fromPos; - if (chars <= 0) return; - - remainder = (int32_t)this->used - toPos; - if (remainder > 0) - memmove(&this->text[fromPos],&this->text[toPos],remainder * sizeof(wchar_t)); - - this->used -= chars; - this->text[this->used] = L'\0'; - this->modified = true; -} // TextBuffer::Delete - -bool TextBuffer::AssertTextSize(size_t deltaSize) { - size_t newSize; - wchar_t *tmpText; - - if (this->used + deltaSize <= this->size) return true; - - newSize = this->size + ((deltaSize + minimalTextBufferSize - 1)/minimalTextBufferSize)*minimalTextBufferSize; - tmpText = (wchar_t *)malloc(newSize * sizeof(wchar_t)); - if (tmpText != NULL) { - memcpy(tmpText,this->text,this->used * sizeof(wchar_t)); - if (this->text != NULL) - free(this->text); - this->size = newSize; - this->text = tmpText; - } - return tmpText != NULL; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define _CRT_SECURE_NO_DEPRECATE +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING + +#include // assert +#include // swprintf +#include // strncpy etc. +#include +#include +#include +#include +#include +#include +#include + +#include "TextBuffer.h" +#ifndef _WIN32 +#define swprintf(wcs, ...) swprintf(wcs, 1024, __VA_ARGS__) +#endif + +#ifndef Min + #define Min(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef Max + #define Max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +TextBuffer::TextBuffer(void) { + this->size = this->used = 0; + this->indent = 0; + this->modified = false; + this->text = NULL; +} // TextBuffer::TextBuffer + +TextBuffer::~TextBuffer(void) { + if (this->text != NULL) free(this->text); +} // TextBuffer::~TextBuffer + +void TextBuffer::Indent(int32_t indent) { + this->indent = Max(0,this->indent + indent); +} // TextBuffer::Indent + +bool TextBuffer::GetModified(void) { + return this->modified; +} // TextBuffer::GetModified + +void TextBuffer::SetModified(bool on) { + this->modified = on; +} // TextBuffer::SetModified + +size_t TextBuffer::Length(void) { + return this->used; +} // TextBuffer::Length + +int32_t TextBuffer::TheLength() +{ + return (int32_t)this->Length(); +} + +int32_t TextBuffer::TheLengthInUTF8(void) +{ + std::string text8; + + std::wstring_convert> converter; + text8 = converter.to_bytes(this->text); + + return static_cast(text8.length()); +} + +int32_t TextBuffer::LineNumOf(int32_t pos) { + wchar_t *text,*textEnd; + int32_t lineNum; + + for (text = this->text, textEnd = text + Max(0,Min(pos,(int32_t)this->used)), lineNum = 0; text < textEnd; text++) + if (*text == textBufferLineBreak) + lineNum++; + + return lineNum; +} // TextBuffer::LineNumOf + +int32_t TextBuffer::PosOf(int32_t lineNum) { + wchar_t *text,*textEnd; + + for (text = this->text, textEnd = text + this->used; lineNum > 0 && text < textEnd; text++) + if (*text == textBufferLineBreak) + lineNum--; + + return (int32_t)(ptrdiff_t)(text - this->text); +} // TextBuffer::PosOf + + +void TextBuffer::GetText(size_t *textLen, char text[]) +{ + std::string text8; + + std::wstring_convert> converter; + text8 = converter.to_bytes(this->text); + + *textLen = static_cast(text8.size()); + if (*textLen > 0) + { + std::copy(text8.begin(), text8.end(), text); + text[text8.size()] = '\0'; // don't forget the terminating 0 + } +} + +void TextBuffer::GetText(size_t *textLen, wchar_t text[]) { + *textLen = this->used; + if (*textLen > 0) wcsncpy(text,this->text,*textLen); + text[*textLen] = L'\0'; +} // TextBuffer::GetText + +void TextBuffer::GetText(std::wstring &text) +{ + text.assign(this->text); +} + +void TextBuffer::SetText(int32_t textLen, const char text[]) +{ + if (textLen == 0) + { + this->SetText(0, L""); + } + else + { + std::string str(reinterpret_cast(text), textLen); + std::wstring_convert> converter; + std::wstring wstr = converter.from_bytes(str); + + this->SetText(wstr.length(), wstr.c_str()); + } +} + +void TextBuffer::SetText(size_t textLen, const wchar_t text[]) { + if (!this->AssertTextSize(textLen + 1 - this->used)) return; + + if (textLen > 0) + wcsncpy(this->text,text,textLen); + this->used = textLen; + this->text[this->used] = L'\0'; + this->modified = false; // !!! +} // TextBuffer::SetText + +wchar_t TextBuffer::GetCh(int32_t atPos) { + return 0 <= atPos && atPos < (int32_t)this->used ? this->text[atPos] : L'\0'; +} // TextBuffer::GetCh + +void TextBuffer::GetLine(int32_t atPos, int32_t *lineLen, wchar_t line[], int32_t *lineSepLen) { + int32_t startPos,endPos,usedLineLen; + + *lineLen = *lineSepLen = 0; + if (atPos < 0 || (int32_t)this->used <= atPos) return; + startPos = atPos; + while (startPos > 0 && this->text[startPos-1] != textBufferLineBreak) startPos--; + endPos = atPos; + while (endPos < (int32_t)this->used && this->text[endPos] != textBufferLineBreak) endPos++; + *lineLen = endPos - startPos; + *lineSepLen = 1; + usedLineLen = Min(*lineLen,0xff); + wcsncpy(line,&this->text[startPos],usedLineLen); + line[usedLineLen] = L'\0'; +} // TextBuffer::GetLine + +void TextBuffer::GetRange(int32_t fromPos, int32_t toPos, wchar_t text[]) { + int32_t chars; + + fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)this->used)); + toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)this->used)); + chars = toPos - fromPos; + if (chars < 0) return; + + if (chars > 0) wcsncpy(text,&this->text[fromPos],chars); + text[chars] = L'\0'; +} // TextBuffer::GetRange + +int32_t TextBuffer::Search(int32_t fromPos, bool wrapAround, wchar_t target[], bool caseSensitive) { +#ifdef _DEBUG + assert(false); +#endif + + return -1; // not found +} // TextBuffer::Search + +void TextBuffer::Append(const wchar_t strg[]) { + size_t chars; + + chars = wcslen(strg); + if (chars <= 0) return; + + if (!this->AssertTextSize(chars + 1)) return; + + if (chars > 0) wcsncpy(&this->text[this->used],strg,chars); + this->used += chars; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::Append + +void TextBuffer::AppendRange(const wchar_t strg[], int32_t fromPos, int32_t toPos) { + size_t chars; + + chars = wcslen(strg); + if (chars <= 0) return; + + fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)chars)); + toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)chars)); + chars = toPos - fromPos; + if (chars <= 0) return; + + if (!this->AssertTextSize(chars + 1)) return; + + wcsncpy(&this->text[this->used],&strg[fromPos],chars); + this->used += chars; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::AppendRange + +void TextBuffer::AppendCh(wchar_t ch) { + if (!this->AssertTextSize(1 + 1)) return; + + this->text[this->used++] = ch; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::AppendCh + +void TextBuffer::AppendLine(const wchar_t strg[]) { + size_t chars, charsEol; + int32_t indent; + wchar_t eol[4]; + + chars = wcslen(strg); + if (chars < 0) return; // hardly, though... + + indent = chars > 0 ? this->indent : 0; + + swprintf(eol,sizeof(eol)/sizeof(wchar_t),L"\r"); + charsEol = wcslen(eol); + + if (!this->AssertTextSize(indent + chars + charsEol + 1)) return; + + if (indent > 0) + memset(&this->text[this->used],L' ',indent * sizeof(wchar_t)); + this->used += indent; + if (chars > 0) + wcsncpy(&this->text[this->used],strg,chars); + this->used += chars; + wcsncpy(&this->text[this->used],eol,charsEol); + this->used += charsEol; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::AppendLine + +void TextBuffer::AppendText(const TextBuffer *text) { + if (text->used > 0) this->AppendRange(text->text,0,(int32_t)text->used); +} // TextBuffer::AppendText + +void TextBuffer::AppendTextRange(const TextBuffer *text, int32_t fromPos, int32_t toPos) { + fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)text->used)); + toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)text->used)); + if (fromPos < toPos) this->AppendRange(text->text,fromPos,toPos); +} // TextBuffer::AppendTextRange + +void TextBuffer::Insert(int32_t atPos, const wchar_t strg[]) { + size_t chars; + int32_t remainder; + + chars = wcslen(strg); + if (chars <= 0) return; + + if (!this->AssertTextSize(chars + 1)) return; + + atPos =(int32_t) Max(0,Min(atPos,(int32_t)this->used)); + + remainder = (int32_t)this->used - atPos; + if (remainder > 0) + memmove(&this->text[atPos + chars],&this->text[atPos],remainder * sizeof(wchar_t)); + + wcsncpy(&this->text[atPos],strg,chars); + this->used += chars; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::Insert + +void TextBuffer::InsertRange(int32_t atPos, const wchar_t strg[], int32_t fromPos, int32_t toPos) { + int32_t remainder; + + size_t chars = wcslen(strg); + if (chars <= 0) return; + + fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)chars)); + toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)chars)); + chars = toPos - fromPos; + if (chars <= 0) return; + + if (!this->AssertTextSize(chars + 1)) return; + + atPos = (int32_t)Max(0,Min(atPos,(int32_t)this->used)); + + remainder = (int32_t)this->used - atPos; + if (remainder > 0) + memmove(&this->text[atPos + chars],&this->text[atPos],remainder * sizeof(wchar_t)); + + wcsncpy(&this->text[atPos],&strg[fromPos],chars); + this->used += chars; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::InsertRange + +void TextBuffer::InsertCh(int32_t atPos, wchar_t ch) { + int32_t remainder; + + if (!this->AssertTextSize(1 + 1)) return; + + atPos = (int32_t)Max(0,Min(atPos,(int32_t)this->used)); + + remainder = (int32_t)this->used - atPos; + if (remainder > 0) + memmove(&this->text[atPos + 1],&this->text[atPos],remainder * sizeof(wchar_t)); + + this->text[atPos] = ch; + this->used++; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::InsertCh + +void TextBuffer::Delete(int32_t fromPos, int32_t toPos) { + int32_t chars,remainder; + + fromPos = (int32_t)Max(0,Min(fromPos,(int32_t)this->used)); + toPos = (int32_t)Max(fromPos,Min(toPos,(int32_t)this->used)); + chars = toPos - fromPos; + if (chars <= 0) return; + + remainder = (int32_t)this->used - toPos; + if (remainder > 0) + memmove(&this->text[fromPos],&this->text[toPos],remainder * sizeof(wchar_t)); + + this->used -= chars; + this->text[this->used] = L'\0'; + this->modified = true; +} // TextBuffer::Delete + +bool TextBuffer::AssertTextSize(size_t deltaSize) { + size_t newSize; + wchar_t *tmpText; + + if (this->used + deltaSize <= this->size) return true; + + newSize = this->size + ((deltaSize + minimalTextBufferSize - 1)/minimalTextBufferSize)*minimalTextBufferSize; + tmpText = (wchar_t *)malloc(newSize * sizeof(wchar_t)); + if (tmpText != NULL) { + memcpy(tmpText,this->text,this->used * sizeof(wchar_t)); + if (this->text != NULL) + free(this->text); + this->size = newSize; + this->text = tmpText; + } + return tmpText != NULL; } // TextBuffer::AssertTextSize \ No newline at end of file diff --git a/src/application.cpp b/src/application.cpp index 112dedf..c6156d4 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,522 +1,521 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS -#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING - -#include - -#include "pch.h" -#include "application.h" - -Application::Application(void) -{ - this->mainFont[0] = L'\0'; - this->textFont[0] = L'\0'; - this->fileName[0] = L'\0'; -} - -bool Application::Create() -{ - //wchar_t errMsg[maxLineSize]; - - this->font = std::make_unique(); - if (this->font == nullptr) - return false; - - this->glyph = std::make_unique(); - if (this->glyph == nullptr) - return false; - - if (!this->font->Create()) - return false; - - this->glyf = std::make_unique(); - if (this->glyf == nullptr) - return false; - - this->prep = std::make_unique(); - if (this->prep == nullptr) - return false; - - this->talk = std::make_unique(); - if (this->talk == nullptr) - return false; - - this->fpgm = std::make_unique(); - if (this->fpgm == nullptr) - return false; - - this->cpgm = std::make_unique(); - if (this->cpgm == nullptr) - return false; - - return true; -} - -Application::~Application(void) -{ - -} - -bool Application::OpenFont(std::string fileName, wchar_t errMsg[]) { - auto file = std::make_unique(); - - this->charCode = this->glyphIndex = INVALID_GLYPH_INDEX; - - this->fileName = fileName; - file->OpenOld(this->fileName.c_str()); - if (file->Error()){ - swprintf(errMsg, L"OpenFont: File Not Found"); - return false; - } - - if (!this->font->Read(file.get(), this->glyph.get(), &this->platformID, &this->encodingID, errMsg)) - return false; - - file->Close(false); - - return true; -} - -bool Application::SaveFont(StripCommand strip, wchar_t errMsg[]) -{ - return this->SaveFont(this->fileName, strip, errMsg); -} - -bool Application::SaveFont(std::string fileN, StripCommand strip, wchar_t errMsg[]) -{ - auto file = std::make_unique(); - errMsg[0] = 0; - - if (!this->BuildFont(strip, errMsg)) - return false; - - file->OpenNew(fileN.c_str()); - if (file->Error()) - return false; - - if (!this->font->Write(file.get(), errMsg)) - file->Close(true); - - file->Close(true); - - return true; -} - -bool Application::GotoFont(wchar_t errMsg[]) { - int32_t errPos, errLen; - bool legacy = false; - - if (!this->font->GetCvt(this->cpgm.get(), errMsg)) return false; - this->font->TheCvt()->Compile(this->cpgm.get(), NULL, legacy, &errPos, &errLen, errMsg); - if (!this->font->GetPrep(this->prep.get(), errMsg)) return false; - if (!this->font->GetFpgm(this->fpgm.get(), errMsg)) return false; - - return true; // by now, ignoring the fact that a stripped font will not have any of the sources above -} // Application::GotoFont - -bool Application::GotoGlyph(int32_t code, bool isGlyphIndex) { - int32_t numGlyphs = this->font->NumberOfGlyphs(), glyphIndex, charCode; - wchar_t errMsg[maxLineSize]; - - if (isGlyphIndex) { - glyphIndex = (code + numGlyphs) % numGlyphs; // cyclical clipping - charCode = this->font->CharCodeOf(glyphIndex); - } - else { - charCode = code; - glyphIndex = this->font->GlyphIndexOf(code); - if (glyphIndex == INVALID_GLYPH_INDEX) - glyphIndex = 0; - } - if (this->glyphIndex != glyphIndex || this->charCode != charCode) { - - this->glyphIndex = glyphIndex; - this->charCode = charCode; - this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - } - return true; // by now, ignoring the fact that a stripped font will not have any of the sources above -} // Application::GotoGlyph - -bool Application::CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]) -{ - bool legacy = false; - - bool done = TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacy, errPos, errLen, errMsg); - - return done; -} - -bool Application::CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]) -{ - bool done = false; - bool legacy = false; - bool variationCompositeGuard = true; - - int32_t glyphIndex, binSize; - unsigned char* binData; - wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; - TextBuffer* errBuf = NULL; - - binData = (unsigned char*)NewP(MAXBINSIZE); - done = binData != NULL; - - glyphIndex = 0; - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. - if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - errBuf = new TextBuffer(); - done = errBuf != NULL; - } - - if (done) { - if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacy, errPos, errLen, compErrMsg)) { - // Compile updates cvt binary autonomously - this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); - } - else { - swprintf(tempErrMsg, L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (this->font->IsVariationFont()) { - this->font->ReverseInterpolateCvarTuples(); - - // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. - auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); - for (auto& tuple : *tuples) - { - // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values - // where the the edited value could have been optomized to another tuple. - bool edited = false; - for (auto& editedValue : tuple->editedCvtValues) - { - edited |= editedValue.Edited(); - } - - // No data and not edits then not needed in cvar - if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied - { - tuple->SetAsCvar(false); - } - } - } - - if (done) { - if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) - done = this->font->UpdateBinData(asmFPGM, binSize, binData); - else { - done = this->font->UpdateBinData(asmFPGM, 0, NULL); - swprintf(tempErrMsg, L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) { - if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg)) - done = this->font->UpdateBinData(asmPREP, binSize, binData); - else { - done = this->font->UpdateBinData(asmPREP, 0, NULL); - swprintf(tempErrMsg, L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(*errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (binData != NULL) DisposeP((void**)&binData); - - done = done && errBuf->Length() == 0; - - if (!done) { - - if (errBuf->Length() > 0) { - std::wstring errStr; - - errBuf->GetText(errStr); - - fwprintf(stderr, errStr.c_str()); - fwprintf(stderr, L"\n"); - } - } - - if (errBuf != NULL) delete errBuf; - - return done; -} - -bool Application::CompileGlyphRange(unsigned short g1, unsigned short g2, bool quiet, wchar_t errMsg[]) -{ - int32_t glyphIndex, binSize, fromGlyph, toGlyph, numGlyphs = this->font->NumberOfGlyphs(); - bool done = false; - unsigned char* binData; - wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; - TextBuffer* errBuf = NULL; - bool legacyCompile = false; - bool variationCompositeGuard = true; - int32_t errPos = 0; - int32_t errLen = 0; - - binData = (unsigned char*)NewP(MAXBINSIZE); - done = binData != NULL; - - glyphIndex = 0; - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - - done = this->CompileCommon(&errPos, &errLen, errMsg); - if (!done) - return done; - - if (done) { - errBuf = new TextBuffer(); - done = errBuf != NULL; - } - - fromGlyph = g1; - toGlyph = g2; - for (glyphIndex = fromGlyph; glyphIndex <= toGlyph && glyphIndex < numGlyphs && done; glyphIndex++) { - if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); - if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); - - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, &errPos, &errLen, compErrMsg)) { - swprintf(tempErrMsg, L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - swprintf(tempErrMsg, L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(errPos), compErrMsg); - this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors - } - } - - binSize = 0; - if (done) { - if (TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) { - done = this->font->UpdateBinData(asmGLYF, binSize, binData); - } - else - { - done = this->font->UpdateBinData(asmGLYF, 0, NULL); - swprintf(tempErrMsg, L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) - { - done = this->BuildFont(stripNothing, compErrMsg); - } - } - if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); - - if (binData != NULL) DisposeP((void**)&binData); - - done = done && errBuf->Length() == 0; - - if (!done) { - if (errBuf->Length() > 0) { - std::wstring errStr; - - errBuf->GetText(errStr); - - fwprintf(stderr, errStr.c_str()); - fwprintf(stderr, L"\n"); - } - } - - if (errBuf != NULL) delete errBuf; - - return done; -} - -bool Application::CompileAll(bool quiet, wchar_t errMsg[]) -{ - int32_t glyphIndex, binSize, fromGlyph, fromChar, numGlyphs = this->font->NumberOfGlyphs(); - bool done; - unsigned char* binData; - wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; - TextBuffer* errBuf = NULL; - bool legacyCompile = false; - bool variationCompositeGuard = true; - int32_t errPos = 0; - int32_t errLen = 0; - - binData = (unsigned char*)NewP(MAXBINSIZE); - done = binData != NULL; - - glyphIndex = 0; - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. - if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - done = this->font->InitIncrBuildSfnt(false, errMsg); - } - - if (done) { - errBuf = new TextBuffer(); - done = errBuf != NULL; - } - - if (done) { - if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacyCompile, &errPos, &errLen, compErrMsg)) { - // Compile updates cvt binary autonomously - this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); - } - else { - swprintf(tempErrMsg, L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - - if (this->font->IsVariationFont()) { - this->font->ReverseInterpolateCvarTuples(); - - // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. - auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); - for (auto& tuple : *tuples) - { - // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values - // where the the edited value could have been optomized to another tuple. - bool edited = false; - for (auto& editedValue : tuple->editedCvtValues) - { - edited |= editedValue.Edited(); - } - - // No data and not edits then not needed in cvar - if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied - { - tuple->SetAsCvar(false); - } - } - } - - if (done) { - if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) - done = this->font->UpdateBinData(asmFPGM, binSize, binData); - else { - done = this->font->UpdateBinData(asmFPGM, 0, NULL); - swprintf(tempErrMsg, L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) { - if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) - done = this->font->UpdateBinData(asmPREP, binSize, binData); - else { - done = this->font->UpdateBinData(asmPREP, 0, NULL); - swprintf(tempErrMsg, L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - fromGlyph = this->glyphIndex; fromChar = this->charCode; - for (glyphIndex = 0; glyphIndex < numGlyphs && done; glyphIndex++) { - if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); - if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); - //this->MakeProgress(glyphIndex, glyphIndex); - this->glyphIndex = glyphIndex; - this->charCode = this->font->CharCodeOf(glyphIndex); - done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg); - - if (done) { - if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, &errPos, &errLen, compErrMsg)) { - swprintf(tempErrMsg, L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - swprintf(tempErrMsg, L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(errPos), compErrMsg); - this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors - } - } - - binSize = 0; - if (done) { - if (!TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg)) { - swprintf(tempErrMsg, L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(errPos), compErrMsg); - errBuf->AppendLine(tempErrMsg); - } - } - - if (done) done = this->font->AddGlyphToNewSfnt(this->font->CharGroupOf(glyphIndex), glyphIndex, this->glyph.get(), binSize, binData, this->glyf.get(), this->talk.get(), errMsg); - } - if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); - - done = this->font->TermIncrBuildSfnt(!done, this->prep.get(), this->cpgm.get(), this->fpgm.get(), errMsg); - - if (binData != NULL) DisposeP((void**)&binData); - - done = done && errBuf->Length() == 0; - - if (!done) { - // this error is supposed to report the reason that precluded further execution - //if (STRLENW(errMsg) > 0) - // ErrorBox(errMsg); - // these errors are supposed to report failed compilations (give the user a chance to fix the code) - if (errBuf->Length() > 0) { - std::wstring errStr; - errBuf->GetText(errStr); - swprintf(errMsg, L"Compile Error: " WIDE_STR_FORMAT, errStr.c_str()); - } - } - - if (errBuf != NULL) delete errBuf; - - return done; -} - -bool Application::BuildFont(StripCommand strip, wchar_t errMsg[]) { - - // If we did not compile and are just here to strip data then perform lazy initialization. - if (this->glyphIndex == INVALID_GLYPH_INDEX) - { - this->glyphIndex = 0; - this->charCode = this->font->CharCodeOf(this->glyphIndex); - } - - // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. - bool done = true; - if (done) done = this->font->GetGlyph(this->glyphIndex, this->glyph.get(), errMsg); - if (done) done = this->font->GetTalk(this->glyphIndex, this->talk.get(), errMsg); - if (done) done = this->font->GetGlyf(this->glyphIndex, this->glyf.get(), errMsg); - - return this->font->BuildNewSfnt(strip, anyGroup, this->glyphIndex, this->glyph.get(), this->glyf.get(), - this->prep.get(), this->cpgm.get(), this->talk.get(), this->fpgm.get(), errMsg); -} - -char* Application::wCharToChar(char out[], const wchar_t in[]) -{ - std::string text8; - - out[0] = '\0'; // just in case - - std::wstring_convert> converter; - text8 = converter.to_bytes(in); - - size_t len = static_cast(text8.size()); - if (len > 0) - { - std::copy(text8.begin(), text8.end(), out); - out[text8.size()] = '\0'; // don't forget the terminating 0 - } - - return out; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define _CRT_SECURE_NO_DEPRECATE +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING + +#include + +#include "pch.h" +#include "application.h" + +Application::Application(void) +{ + this->mainFont[0] = L'\0'; + this->textFont[0] = L'\0'; + this->fileName[0] = L'\0'; +} + +bool Application::Create() +{ + //wchar_t errMsg[maxLineSize]; + + this->font = std::make_unique(); + if (this->font == nullptr) + return false; + + this->glyph = std::make_unique(); + if (this->glyph == nullptr) + return false; + + if (!this->font->Create()) + return false; + + this->glyf = std::make_unique(); + if (this->glyf == nullptr) + return false; + + this->prep = std::make_unique(); + if (this->prep == nullptr) + return false; + + this->talk = std::make_unique(); + if (this->talk == nullptr) + return false; + + this->fpgm = std::make_unique(); + if (this->fpgm == nullptr) + return false; + + this->cpgm = std::make_unique(); + if (this->cpgm == nullptr) + return false; + + return true; +} + +Application::~Application(void) +{ + +} + +bool Application::OpenFont(std::string fileName, wchar_t errMsg[], size_t errMsgLen) { + auto file = std::make_unique(); + + this->charCode = this->glyphIndex = INVALID_GLYPH_INDEX; + + this->fileName = fileName; + file->OpenOld(this->fileName.c_str()); + if (file->Error()){ + swprintf(errMsg, errMsgLen, L"OpenFont: File Not Found"); + return false; + } + + if (!this->font->Read(file.get(), this->glyph.get(), &this->platformID, &this->encodingID, errMsg, errMsgLen)) + return false; + + file->Close(false); + + return true; +} + +bool Application::SaveFont(StripCommand strip, wchar_t errMsg[], size_t errMsgLen) +{ + return this->SaveFont(this->fileName, strip, errMsg, errMsgLen); +} + +bool Application::SaveFont(std::string fileN, StripCommand strip, wchar_t errMsg[], size_t errMsgLen) +{ + auto file = std::make_unique(); + errMsg[0] = 0; + + if (!this->BuildFont(strip, errMsg, errMsgLen)) + return false; + + file->OpenNew(fileN.c_str()); + if (file->Error()) + return false; + + if (!this->font->Write(file.get(), errMsg, errMsgLen)) + file->Close(true); + + file->Close(true); + + return true; +} + +bool Application::GotoFont(wchar_t errMsg[], size_t errMsgLen) { + int32_t errPos, errLen; + bool legacy = false; + + if (!this->font->GetCvt(this->cpgm.get(), errMsg, errMsgLen)) return false; + this->font->TheCvt()->Compile(this->cpgm.get(), NULL, legacy, &errPos, &errLen, errMsg, errMsgLen); + if (!this->font->GetPrep(this->prep.get(), errMsg, errMsgLen)) return false; + if (!this->font->GetFpgm(this->fpgm.get(), errMsg, errMsgLen)) return false; + + return true; // by now, ignoring the fact that a stripped font will not have any of the sources above +} // Application::GotoFont + +bool Application::GotoGlyph(int32_t code, bool isGlyphIndex) { + int32_t numGlyphs = this->font->NumberOfGlyphs(), glyphIndex, charCode; + wchar_t errMsg[maxLineSize]; + + if (isGlyphIndex) { + glyphIndex = (code + numGlyphs) % numGlyphs; // cyclical clipping + charCode = this->font->CharCodeOf(glyphIndex); + } + else { + charCode = code; + glyphIndex = this->font->GlyphIndexOf(code); + if (glyphIndex == INVALID_GLYPH_INDEX) + glyphIndex = 0; + } + if (this->glyphIndex != glyphIndex || this->charCode != charCode) { + + this->glyphIndex = glyphIndex; + this->charCode = charCode; + this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg, sizeof(errMsg)/sizeof(wchar_t)); + this->font->GetTalk(glyphIndex, this->talk.get(), errMsg, sizeof(errMsg)/sizeof(wchar_t)); + } + return true; // by now, ignoring the fact that a stripped font will not have any of the sources above +} // Application::GotoGlyph + +bool Application::CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[], size_t errMsgLen) +{ + bool legacy = false; + + bool done = TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacy, errPos, errLen, errMsg, errMsgLen); + + return done; +} + +bool Application::CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[], size_t errMsgLen) +{ + bool done = false; + bool legacy = false; + bool variationCompositeGuard = true; + + int32_t glyphIndex, binSize; + unsigned char* binData; + wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; + TextBuffer* errBuf = NULL; + + binData = (unsigned char*)NewP(MAXBINSIZE); + done = binData != NULL; + + glyphIndex = 0; + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. + if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg, errMsgLen); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg, errMsgLen); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg, errMsgLen); + + if (done) { + errBuf = new TextBuffer(); + done = errBuf != NULL; + } + + if (done) { + if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacy, errPos, errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) { + // Compile updates cvt binary autonomously + this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); + } + else { + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(*errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (this->font->IsVariationFont()) { + this->font->ReverseInterpolateCvarTuples(); + + // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. + auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); + for (auto& tuple : *tuples) + { + // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values + // where the the edited value could have been optomized to another tuple. + bool edited = false; + for (auto& editedValue : tuple->editedCvtValues) + { + edited |= editedValue.Edited(); + } + + // No data and not edits then not needed in cvar + if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied + { + tuple->SetAsCvar(false); + } + } + } + + if (done) { + if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) + done = this->font->UpdateBinData(asmFPGM, binSize, binData); + else { + done = this->font->UpdateBinData(asmFPGM, 0, NULL); + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t),L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(*errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) { + if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, errPos, errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) + done = this->font->UpdateBinData(asmPREP, binSize, binData); + else { + done = this->font->UpdateBinData(asmPREP, 0, NULL); + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(*errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (binData != NULL) DisposeP((void**)&binData); + + done = done && errBuf->Length() == 0; + + if (!done) { + + if (errBuf->Length() > 0) { + std::wstring errStr; + + errBuf->GetText(errStr); + + fwprintf(stderr, errStr.c_str()); + fwprintf(stderr, L"\n"); + } + } + + if (errBuf != NULL) delete errBuf; + + return done; +} + +bool Application::CompileGlyphRange(unsigned short g1, unsigned short g2, bool quiet, wchar_t errMsg[], size_t errMsgLen) +{ + int32_t glyphIndex, binSize, fromGlyph, toGlyph, numGlyphs = this->font->NumberOfGlyphs(); + bool done = false; + unsigned char* binData; + wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; + TextBuffer* errBuf = NULL; + bool legacyCompile = false; + bool variationCompositeGuard = true; + int32_t errPos = 0; + int32_t errLen = 0; + + binData = (unsigned char*)NewP(MAXBINSIZE); + done = binData != NULL; + + glyphIndex = 0; + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + + done = this->CompileCommon(&errPos, &errLen, errMsg, errMsgLen); + if (!done) + return done; + + if (done) { + errBuf = new TextBuffer(); + done = errBuf != NULL; + } + + fromGlyph = g1; + toGlyph = g2; + for (glyphIndex = fromGlyph; glyphIndex <= toGlyph && glyphIndex < numGlyphs && done; glyphIndex++) { + if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); + if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); + + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg, errMsgLen); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg, errMsgLen); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg, errMsgLen); + + if (done) { + if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, &errPos, &errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) { + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(errPos), compErrMsg); + this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors + } + } + + binSize = 0; + if (done) { + if (TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) { + done = this->font->UpdateBinData(asmGLYF, binSize, binData); + } + else + { + done = this->font->UpdateBinData(asmGLYF, 0, NULL); + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) + { + done = this->BuildFont(stripNothing, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t)); + } + } + if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); + + if (binData != NULL) DisposeP((void**)&binData); + + done = done && errBuf->Length() == 0; + + if (!done) { + if (errBuf->Length() > 0) { + std::wstring errStr; + + errBuf->GetText(errStr); + + fwprintf(stderr, errStr.c_str()); + fwprintf(stderr, L"\n"); + } + } + + if (errBuf != NULL) delete errBuf; + + return done; +} + +bool Application::CompileAll(bool quiet, wchar_t errMsg[], size_t errMsgLen) +{ + int32_t glyphIndex, binSize, fromGlyph, fromChar, numGlyphs = this->font->NumberOfGlyphs(); + bool done; + unsigned char* binData; + wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; + TextBuffer* errBuf = NULL; + bool legacyCompile = false; + bool variationCompositeGuard = true; + int32_t errPos = 0; + int32_t errLen = 0; + + binData = (unsigned char*)NewP(MAXBINSIZE); + done = binData != NULL; + + glyphIndex = 0; + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. + if (done) done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg, errMsgLen); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg, errMsgLen); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg, errMsgLen); + + if (done) { + done = this->font->InitIncrBuildSfnt(false, errMsg, errMsgLen); + } + + if (done) { + errBuf = new TextBuffer(); + done = errBuf != NULL; + } + + if (done) { + if (this->font->TheCvt()->Compile(this->cpgm.get(), this->prep.get(), legacyCompile, &errPos, &errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) { + // Compile updates cvt binary autonomously + this->font->UpdateAdvanceWidthFlag(this->font->TheCvt()->LinearAdvanceWidths()); + } + else { + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"Ctrl Pgm, line %li: " WIDE_STR_FORMAT, this->cpgm->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + + if (this->font->IsVariationFont()) { + this->font->ReverseInterpolateCvarTuples(); + + // Check tuples that don't result in any deltas and and are not represented in "edited" to reset relavence for cvar. + auto tuples = this->font->GetInstanceManager()->GetCvarTuples(); + for (auto& tuple : *tuples) + { + // Even though a tuple may have no deltas make sure we don't remove any instanced with user edited values + // where the the edited value could have been optomized to another tuple. + bool edited = false; + for (auto& editedValue : tuple->editedCvtValues) + { + edited |= editedValue.Edited(); + } + + // No data and not edits then not needed in cvar + if (!tuple->HasData() && !edited) // && tuple->IsCvar()) // implied + { + tuple->SetAsCvar(false); + } + } + } + + if (done) { + if (TTAssemble(asmFPGM, this->fpgm.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) + done = this->font->UpdateBinData(asmFPGM, binSize, binData); + else { + done = this->font->UpdateBinData(asmFPGM, 0, NULL); + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(tempErrMsg), L"Font Pgm, line %li: " WIDE_STR_FORMAT, this->fpgm->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) { + if (TTAssemble(asmPREP, this->prep.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) + done = this->font->UpdateBinData(asmPREP, binSize, binData); + else { + done = this->font->UpdateBinData(asmPREP, 0, NULL); + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"Pre Pgm, line %li: " WIDE_STR_FORMAT, this->prep->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + fromGlyph = this->glyphIndex; fromChar = this->charCode; + for (glyphIndex = 0; glyphIndex < numGlyphs && done; glyphIndex++) { + if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); + if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); + //this->MakeProgress(glyphIndex, glyphIndex); + this->glyphIndex = glyphIndex; + this->charCode = this->font->CharCodeOf(glyphIndex); + done = this->font->GetGlyph(glyphIndex, this->glyph.get(), errMsg, errMsgLen); + if (done) done = this->font->GetTalk(glyphIndex, this->talk.get(), errMsg, errMsgLen); + if (done) done = this->font->GetGlyf(glyphIndex, this->glyf.get(), errMsg, errMsgLen); + + if (done) { + if (!TMTCompile(this->talk.get(), this->font.get(), this->glyph.get(), this->glyphIndex, this->glyf.get(), legacyCompile, &errPos, &errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) { + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"VTT Talk, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->talk->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"/* Error in VTT Talk, line %li: " WIDE_STR_FORMAT L" */", this->talk->LineNumOf(errPos), compErrMsg); + this->glyf->SetText((int32_t)STRLENW(tempErrMsg), tempErrMsg); // prevent follow-up errors + } + } + + binSize = 0; + if (done) { + if (!TTAssemble(asmGLYF, this->glyf.get(), this->font.get(), this->glyph.get(), MAXBINSIZE, binData, &binSize, variationCompositeGuard, &errPos, &errLen, compErrMsg, sizeof(compErrMsg)/sizeof(wchar_t))) { + swprintf(tempErrMsg, sizeof(tempErrMsg)/sizeof(wchar_t), L"Glyf Pgm, glyph %li (Unicode 0x%lx), line %li: " WIDE_STR_FORMAT, this->glyphIndex, this->charCode, this->glyf->LineNumOf(errPos), compErrMsg); + errBuf->AppendLine(tempErrMsg); + } + } + + if (done) done = this->font->AddGlyphToNewSfnt(this->font->CharGroupOf(glyphIndex), glyphIndex, this->glyph.get(), binSize, binData, this->glyf.get(), this->talk.get(), errMsg, errMsgLen); + } + if (!quiet && (glyphIndex % 100 != 0)) wprintf_s(L"\n"); + + done = this->font->TermIncrBuildSfnt(!done, this->prep.get(), this->cpgm.get(), this->fpgm.get(), errMsg, errMsgLen); + + if (binData != NULL) DisposeP((void**)&binData); + + done = done && errBuf->Length() == 0; + + if (!done) { + // this error is supposed to report the reason that precluded further execution + //if (STRLENW(errMsg) > 0) + // ErrorBox(errMsg); + // these errors are supposed to report failed compilations (give the user a chance to fix the code) + if (errBuf->Length() > 0) { + std::wstring errStr; + errBuf->GetText(errStr); + swprintf(errMsg, errMsgLen, L"Compile Error: " WIDE_STR_FORMAT, errStr.c_str()); + } + } + + if (errBuf != NULL) delete errBuf; + + return done; +} + +bool Application::BuildFont(StripCommand strip, wchar_t errMsg[], size_t errMsgLen) { + + // If we did not compile and are just here to strip data then perform lazy initialization. + if (this->glyphIndex == INVALID_GLYPH_INDEX) + { + this->glyphIndex = 0; + this->charCode = this->font->CharCodeOf(this->glyphIndex); + } + + // Init the glyph structure to valid data needed by assembler when assembling fpgm and prep. + bool done = true; + if (done) done = this->font->GetGlyph(this->glyphIndex, this->glyph.get(), errMsg, errMsgLen); + if (done) done = this->font->GetTalk(this->glyphIndex, this->talk.get(), errMsg, errMsgLen); + if (done) done = this->font->GetGlyf(this->glyphIndex, this->glyf.get(), errMsg, errMsgLen); + + return this->font->BuildNewSfnt(strip, anyGroup, this->glyphIndex, this->glyph.get(), this->glyf.get(), + this->prep.get(), this->cpgm.get(), this->talk.get(), this->fpgm.get(), errMsg, errMsgLen); +} + +char* Application::wCharToChar(char out[], const wchar_t in[]) +{ + std::string text8; + + out[0] = '\0'; // just in case + + std::wstring_convert> converter; + text8 = converter.to_bytes(in); + + size_t len = static_cast(text8.size()); + if (len > 0) + { + std::copy(text8.begin(), text8.end(), out); + out[text8.size()] = '\0'; // don't forget the terminating 0 + } + + return out; } \ No newline at end of file diff --git a/src/application.h b/src/application.h index 01b88dd..7de63da 100644 --- a/src/application.h +++ b/src/application.h @@ -1,41 +1,41 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once - -class Application -{ -public: - Application(void); - bool Create(); - virtual ~Application(void); - - bool OpenFont(std::string fileName, wchar_t errMsg[]); - bool SaveFont(StripCommand strip, wchar_t errMsg[]); - bool SaveFont(std::string fileName, StripCommand strip, wchar_t errMsg[]); - - bool GotoFont(wchar_t errMsg[]); - bool GotoGlyph(int32_t code, bool isGlyphIndex); - - bool CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]); - bool CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]); - - bool CompileGlyphRange(unsigned short g1, unsigned short g2, bool quiet, wchar_t errMsg[]); - bool CompileAll(bool quiet, wchar_t errMsg[]); - - bool BuildFont(StripCommand strip, wchar_t errMsg[]); - - char* wCharToChar(char out[], const wchar_t in[]); - -private: - - std::unique_ptr glyf = nullptr, prep = nullptr, talk = nullptr, fpgm = nullptr, cpgm = nullptr; - short platformID = 3, encodingID = 1; - - wchar_t mainFont[maxLineSize], textFont[maxLineSize]; - - std::unique_ptr font = nullptr; - std::unique_ptr glyph = nullptr; - std::string fileName; - int32_t charCode = 0, glyphIndex = 0; -}; - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +class Application +{ +public: + Application(void); + bool Create(); + virtual ~Application(void); + + bool OpenFont(std::string fileName, wchar_t errMsg[], size_t errMsgLen); + bool SaveFont(StripCommand strip, wchar_t errMsg[], size_t errMsgLen); + bool SaveFont(std::string fileName, StripCommand strip, wchar_t errMsg[], size_t errMsgLen); + + bool GotoFont(wchar_t errMsg[], size_t errMsgLen); + bool GotoGlyph(int32_t code, bool isGlyphIndex); + + bool CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[], size_t errMsgLen); + bool CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[], size_t errMsgLen); + + bool CompileGlyphRange(unsigned short g1, unsigned short g2, bool quiet, wchar_t errMsg[], size_t errMsgLen); + bool CompileAll(bool quiet, wchar_t errMsg[], size_t errMsgLen); + + bool BuildFont(StripCommand strip, wchar_t errMsg[], size_t errMsgLen); + + char* wCharToChar(char out[], const wchar_t in[]); + +private: + + std::unique_ptr glyf = nullptr, prep = nullptr, talk = nullptr, fpgm = nullptr, cpgm = nullptr; + short platformID = 3, encodingID = 1; + + wchar_t mainFont[maxLineSize], textFont[maxLineSize]; + + std::unique_ptr font = nullptr; + std::unique_ptr glyph = nullptr; + std::string fileName; + int32_t charCode = 0, glyphIndex = 0; +}; + diff --git a/src/pch.h b/src/pch.h index b6af5d9..37c0a43 100644 --- a/src/pch.h +++ b/src/pch.h @@ -1,66 +1,65 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#define maxLineSize 0x100 - -#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "opentypedefs.h" -#include "Platform.h" -#include "FixedMath.h" -#include "MathUtils.h" -#include "List.h" -#include "Memory.h" -#include "File.h" -#include "TextBuffer.h" -#include "Variation.h" -#include "VariationModels.h" -#include "VariationInstance.h" -#include "TTFont.h" -#include "GUIDecorations.h" -#include "TTAssembler.h" -#include "TTEngine.h" -#include "TTGenerator.h" -#include "CvtManager.h" -#include "TMTParser.h" - - -#define STRCPYW wcscpy -#define STRCATW wcscat -#define STRLENW wcslen -#define STRNCPYW wcsncpy -#define STRSTRW wcsstr -#define STRCHARW wcschr -#define STRCMPW wcscmp - -#ifndef _WIN32 -#define swprintf(wcs, ...) swprintf(wcs, 1024, __VA_ARGS__) -#define wprintf_s wprintf -#endif - -#ifndef _MSC_VER -/* ISO C Standard for *w*printf() */ -#define WIDE_STR_FORMAT L"%S" -#define NARROW_STR_FORMAT L"%s" -#else -/* Microsoft compiler's w*printf() behavior */ -#define WIDE_STR_FORMAT L"%s" -#define NARROW_STR_FORMAT L"%S" -#endif +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#define maxLineSize 0x100 + +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "opentypedefs.h" +#include "Platform.h" +#include "FixedMath.h" +#include "MathUtils.h" +#include "List.h" +#include "Memory.h" +#include "File.h" +#include "TextBuffer.h" +#include "Variation.h" +#include "VariationModels.h" +#include "VariationInstance.h" +#include "TTFont.h" +#include "GUIDecorations.h" +#include "TTAssembler.h" +#include "TTEngine.h" +#include "TTGenerator.h" +#include "CvtManager.h" +#include "TMTParser.h" + + +#define STRCPYW wcscpy +#define STRCATW wcscat +#define STRLENW wcslen +#define STRNCPYW wcsncpy +#define STRSTRW wcsstr +#define STRCHARW wcschr +#define STRCMPW wcscmp + +#ifndef _WIN32 +#define wprintf_s wprintf +#endif + +#ifndef _MSC_VER +/* ISO C Standard for *w*printf() */ +#define WIDE_STR_FORMAT L"%S" +#define NARROW_STR_FORMAT L"%s" +#else +/* Microsoft compiler's w*printf() behavior */ +#define WIDE_STR_FORMAT L"%s" +#define NARROW_STR_FORMAT L"%S" +#endif diff --git a/src/vttcompile.cpp b/src/vttcompile.cpp index f2db24f..e439901 100644 --- a/src/vttcompile.cpp +++ b/src/vttcompile.cpp @@ -1,218 +1,217 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_NON_CONFORMING_SWPRINTFS - -#include - -#include "pch.h" -#include "application.h" - -#include "getopt.h" - -int ShowUsage(wchar_t* strErr) -{ - if (strErr) - { - wprintf(L"ERROR: " WIDE_STR_FORMAT L"\n\n", strErr); - } - wprintf(L"USAGE: vttcompile [options] [out.ttf] \n"); - wprintf(L"\t-a compile everything \n"); - wprintf(L"\t-gXXXX compile everything for glyph id (base 10) \n"); - wprintf(L"\t-rXXXX compile everything for glyph range starting with glyph specified \n\t with -g up to and including glyph specified with -r \n "); - wprintf(L"\t-s strip source \n"); - wprintf(L"\t-b strip source and hints \n"); - wprintf(L"\t-c strip source, hints and cache tables \n"); - wprintf(L"\t-q quiet mode \n"); - wprintf(L"\t-? this help message \n\n"); - wprintf(L"\n"); - return -1; -} - -int main(int argc, char* argv[]) -{ - char c; - bool bStripSource = false; - bool bStripHints = false; - bool bStripCache = false; - bool bCompileAll = false; - bool bQuiet = false; - std::string sg1, sg2; - uint32_t g1 = 0, g2 = 0; - bool haveGlyph = false; - bool haveRange = false; - - int argOffset = 0; - - wchar_t errMsg[1024]; - errMsg[0] = 0; - - std::string inFile; - std::string outFile; - - std::wstring szOutText; - - wprintf(L"Microsoft Visual TrueType Command Line Interface Version " VTTVersionString L" \n"); - wprintf(L"Copyright (C) Microsoft Corporation. Licensed under the MIT License.\n"); - - if (argc == 1) return ShowUsage(NULL); - - CommandLineOptions cmd(argc, argv, "?HhAaBbSsCcqQg:G:r:R"); - - while ((c = cmd.GetOption()) != END) - { - switch (c) - { - case 'A': - case 'a': - bCompileAll = true; - break; - - case 'B': - case 'b': - bStripHints = true; - break; - - case 'S': - case 's': - bStripSource = true; - break; - - case 'c': - case 'C': - bStripCache = true; - break; - - case 'q': - case 'Q': - bQuiet = true; - break; - - case 'g': - case 'G': - sg1 = cmd.GetOptionArgument(); - break; - - case 'r': - case 'R': - sg2 = cmd.GetOptionArgument(); - break; - - case 'H': - case 'h': - case '?': - default: - ShowUsage(NULL); - exit(EXIT_SUCCESS); - } - } - - if (argv[cmd.GetArgumentIndex() + argOffset] == NULL) - { - ShowUsage(NULL); - exit(EXIT_SUCCESS); - } - - if (!sg1.empty()) - { - try - { - g1 = g2 = static_cast(std::stoul(sg1)); - } - catch (...) - { - fwprintf(stderr, L"Can not parse glyph number!\n"); - exit(EXIT_FAILURE); - } - haveGlyph = true; - } - - if (!sg2.empty()) - { - try - { - g2 = static_cast(std::stoul(sg2)); - } - catch (...) - { - fwprintf(stderr, L"Can not parse glyph number!\n"); - exit(EXIT_FAILURE); - } - haveRange = true; - } - - inFile = argv[cmd.GetArgumentIndex() + argOffset]; - argOffset++; - - if (argv[cmd.GetArgumentIndex() + argOffset] == NULL) - outFile = inFile; - else - outFile = argv[cmd.GetArgumentIndex() + argOffset]; - - auto application = std::make_unique(); - if (!application->Create()) { - fwprintf(stderr, L"Can not load application!\n"); - exit(EXIT_FAILURE); - } - - if (!application->OpenFont(inFile, errMsg)) - { - fwprintf(stderr, errMsg); - fwprintf(stderr, L"\n"); - fwprintf(stderr, L"Can not open font file " NARROW_STR_FORMAT L"!\n", inFile.c_str()); - exit(EXIT_FAILURE); - } - - if (!application->GotoFont(errMsg)) - { - fwprintf(stderr, errMsg); - fwprintf(stderr, L"Can not initialize font file! \n"); - fwprintf(stderr, L"\n"); - exit(EXIT_FAILURE); - } - - if (bCompileAll) - { - if (!application->CompileAll(bQuiet, errMsg)) - { - fwprintf(stderr, errMsg); - fwprintf(stderr, L"\n"); - fwprintf(stderr, L"Can not complete compile operation! \n"); - fwprintf(stderr, L"\n"); - exit(EXIT_FAILURE); - } - } - else if (haveGlyph) - { - if (!application->CompileGlyphRange((unsigned short)g1, (unsigned short)g2, bQuiet, errMsg)) - { - fwprintf(stderr, errMsg); - fwprintf(stderr, L"\n"); - fwprintf(stderr, L"Can not complete compile operation! \n"); - fwprintf(stderr, L"\n"); - exit(EXIT_FAILURE); - } - } - - StripCommand strip = stripNothing; - if (bStripCache) strip = stripBinary; - else if (bStripHints) strip = stripHints; - else if (bStripSource) strip = stripSource; - - if (!application->SaveFont(outFile, strip, errMsg)) - { - fwprintf(stderr, errMsg); - fwprintf(stderr, L"\n"); - fwprintf(stderr, L"Can not save file! \n"); - fwprintf(stderr, L"\n"); - exit(EXIT_FAILURE); - } - - wprintf(L"Success \n"); - - exit(EXIT_SUCCESS); - return 0; -} - - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#include "pch.h" +#include "application.h" + +#include "getopt.h" + +int ShowUsage(wchar_t* strErr) +{ + if (strErr) + { + wprintf(L"ERROR: " WIDE_STR_FORMAT L"\n\n", strErr); + } + wprintf(L"USAGE: vttcompile [options] [out.ttf] \n"); + wprintf(L"\t-a compile everything \n"); + wprintf(L"\t-gXXXX compile everything for glyph id (base 10) \n"); + wprintf(L"\t-rXXXX compile everything for glyph range starting with glyph specified \n\t with -g up to and including glyph specified with -r \n "); + wprintf(L"\t-s strip source \n"); + wprintf(L"\t-b strip source and hints \n"); + wprintf(L"\t-c strip source, hints and cache tables \n"); + wprintf(L"\t-q quiet mode \n"); + wprintf(L"\t-? this help message \n\n"); + wprintf(L"\n"); + return -1; +} + +int main(int argc, char* argv[]) +{ + char c; + bool bStripSource = false; + bool bStripHints = false; + bool bStripCache = false; + bool bCompileAll = false; + bool bQuiet = false; + std::string sg1, sg2; + uint32_t g1 = 0, g2 = 0; + bool haveGlyph = false; + bool haveRange = false; + + int argOffset = 0; + + wchar_t errMsg[1024]; + errMsg[0] = 0; + + std::string inFile; + std::string outFile; + + std::wstring szOutText; + + wprintf(L"Microsoft Visual TrueType Command Line Interface Version " VTTVersionString L" \n"); + wprintf(L"Copyright (C) Microsoft Corporation. Licensed under the MIT License.\n"); + + if (argc == 1) return ShowUsage(NULL); + + CommandLineOptions cmd(argc, argv, "?HhAaBbSsCcqQg:G:r:R"); + + while ((c = cmd.GetOption()) != END) + { + switch (c) + { + case 'A': + case 'a': + bCompileAll = true; + break; + + case 'B': + case 'b': + bStripHints = true; + break; + + case 'S': + case 's': + bStripSource = true; + break; + + case 'c': + case 'C': + bStripCache = true; + break; + + case 'q': + case 'Q': + bQuiet = true; + break; + + case 'g': + case 'G': + sg1 = cmd.GetOptionArgument(); + break; + + case 'r': + case 'R': + sg2 = cmd.GetOptionArgument(); + break; + + case 'H': + case 'h': + case '?': + default: + ShowUsage(NULL); + exit(EXIT_SUCCESS); + } + } + + if (argv[cmd.GetArgumentIndex() + argOffset] == NULL) + { + ShowUsage(NULL); + exit(EXIT_SUCCESS); + } + + if (!sg1.empty()) + { + try + { + g1 = g2 = static_cast(std::stoul(sg1)); + } + catch (...) + { + fwprintf(stderr, L"Can not parse glyph number!\n"); + exit(EXIT_FAILURE); + } + haveGlyph = true; + } + + if (!sg2.empty()) + { + try + { + g2 = static_cast(std::stoul(sg2)); + } + catch (...) + { + fwprintf(stderr, L"Can not parse glyph number!\n"); + exit(EXIT_FAILURE); + } + haveRange = true; + } + + inFile = argv[cmd.GetArgumentIndex() + argOffset]; + argOffset++; + + if (argv[cmd.GetArgumentIndex() + argOffset] == NULL) + outFile = inFile; + else + outFile = argv[cmd.GetArgumentIndex() + argOffset]; + + auto application = std::make_unique(); + if (!application->Create()) { + fwprintf(stderr, L"Can not load application!\n"); + exit(EXIT_FAILURE); + } + + if (!application->OpenFont(inFile, errMsg, sizeof(errMsg)/sizeof(wchar_t))) + { + fwprintf(stderr, errMsg); + fwprintf(stderr, L"\n"); + fwprintf(stderr, L"Can not open font file " NARROW_STR_FORMAT L"!\n", inFile.c_str()); + exit(EXIT_FAILURE); + } + + if (!application->GotoFont(errMsg, sizeof(errMsg) / sizeof(wchar_t))) + { + fwprintf(stderr, errMsg); + fwprintf(stderr, L"Can not initialize font file! \n"); + fwprintf(stderr, L"\n"); + exit(EXIT_FAILURE); + } + + if (bCompileAll) + { + if (!application->CompileAll(bQuiet, errMsg, sizeof(errMsg) / sizeof(wchar_t))) + { + fwprintf(stderr, errMsg); + fwprintf(stderr, L"\n"); + fwprintf(stderr, L"Can not complete compile operation! \n"); + fwprintf(stderr, L"\n"); + exit(EXIT_FAILURE); + } + } + else if (haveGlyph) + { + if (!application->CompileGlyphRange((unsigned short)g1, (unsigned short)g2, bQuiet, errMsg, sizeof(errMsg) / sizeof(wchar_t))) + { + fwprintf(stderr, errMsg); + fwprintf(stderr, L"\n"); + fwprintf(stderr, L"Can not complete compile operation! \n"); + fwprintf(stderr, L"\n"); + exit(EXIT_FAILURE); + } + } + + StripCommand strip = stripNothing; + if (bStripCache) strip = stripBinary; + else if (bStripHints) strip = stripHints; + else if (bStripSource) strip = stripSource; + + if (!application->SaveFont(outFile, strip, errMsg, sizeof(errMsg) / sizeof(wchar_t))) + { + fwprintf(stderr, errMsg); + fwprintf(stderr, L"\n"); + fwprintf(stderr, L"Can not save file! \n"); + fwprintf(stderr, L"\n"); + exit(EXIT_FAILURE); + } + + wprintf(L"Success \n"); + + exit(EXIT_SUCCESS); + return 0; +} + + From 527f2ce4d42f8ec0ffcffe354658751a36aea168 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 7 Dec 2021 12:31:02 -0800 Subject: [PATCH 13/25] Update Cython structures with new error buffer size plumbing --- vttcompilepy/cvttcompilepy.pxd | 12 ++++++------ vttcompilepy/vttcompilepy.pyx | 34 ++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/vttcompilepy/cvttcompilepy.pxd b/vttcompilepy/cvttcompilepy.pxd index fe829b2..0d55ae7 100644 --- a/vttcompilepy/cvttcompilepy.pxd +++ b/vttcompilepy/cvttcompilepy.pxd @@ -19,13 +19,13 @@ cdef extern from "application.h": cdef cppclass Application: Application() except + bool Create() - bool OpenFont(string fileName, wchar_t* errMsg) - bool SaveFont(StripCommand strip, wchar_t* errMsg) - bool SaveFont(string fileName, StripCommand strip, wchar_t* errMsg) + bool OpenFont(string fileName, wchar_t* errMsg, size_t errMsgLen) + bool SaveFont(StripCommand strip, wchar_t* errMsg, size_t errMsgLen) + bool SaveFont(string fileName, StripCommand strip, wchar_t* errMsg, size_t errMsgLen) - bool GotoFont(wchar_t* errMsg) + bool GotoFont(wchar_t* errMsg, size_t errMsgLen) - bool CompileGlyphRange(uint16_t g1, uint16_t g2, bool quiet, wchar_t* errMsg) - bool CompileAll(bool quiet, wchar_t* errMsg) + bool CompileGlyphRange(uint16_t g1, uint16_t g2, bool quiet, wchar_t* errMsg, size_t errMsgLen) + bool CompileAll(bool quiet, wchar_t* errMsg, size_t errMsgLen) char* wCharToChar(char* out1, const wchar_t* in1) diff --git a/vttcompilepy/vttcompilepy.pyx b/vttcompilepy/vttcompilepy.pyx index d948771..646e35a 100644 --- a/vttcompilepy/vttcompilepy.pyx +++ b/vttcompilepy/vttcompilepy.pyx @@ -4,6 +4,8 @@ from enum import IntEnum from .cvttcompilepy cimport * from pathlib import Path +DEF ERR_BUF_SIZE = 1024 + class CompileError(Exception): pass @@ -24,12 +26,12 @@ cdef class Compiler: self.app_ = new Application() self.app_.Create() cdef string src = bytes(path) - cdef wchar_t werr[1024] - cdef char err[1024] - result = self.app_.OpenFont(src, werr) + cdef wchar_t werr[ERR_BUF_SIZE] + cdef char err[ERR_BUF_SIZE] + result = self.app_.OpenFont(src, werr, ERR_BUF_SIZE) if result != True: raise FileNotFoundError(self.app_.wCharToChar(err, werr)) - result = self.app_.GotoFont(werr) + result = self.app_.GotoFont(werr, ERR_BUF_SIZE) if result != True: raise FileNotFoundError(self.app_.wCharToChar(err, werr)) @@ -37,31 +39,31 @@ cdef class Compiler: del self.app_ def compile_all(self) -> None: - cdef wchar_t werr[1024] - cdef char err[1024] - result = self.app_.CompileAll(True, werr) + cdef wchar_t werr[ERR_BUF_SIZE] + cdef char err[ERR_BUF_SIZE] + result = self.app_.CompileAll(True, werr, ERR_BUF_SIZE) if(result != True): raise CompileError(self.app_.wCharToChar(err, werr)) def compile_glyph_range(self, start: int = 0, end: int = 0) -> None: - cdef wchar_t werr[1024] - cdef char err[1024] - result = self.app_.CompileGlyphRange(start, end, True, werr) + cdef wchar_t werr[ERR_BUF_SIZE] + cdef char err[ERR_BUF_SIZE] + result = self.app_.CompileGlyphRange(start, end, True, werr, ERR_BUF_SIZE) if(result != True): raise CompileError(self.app_.wCharToChar(err, werr)) def save_font(self, path: Path, level: StripLevel) -> None: cdef string dest = bytes(path) - cdef wchar_t werr[1024] - cdef char err[1024] - result = self.app_.SaveFont(dest, level, werr) + cdef wchar_t werr[ERR_BUF_SIZE] + cdef char err[ERR_BUF_SIZE] + result = self.app_.SaveFont(dest, level, werr, ERR_BUF_SIZE) if(result != True): raise FileNotFoundError(self.app_.wCharToChar(err, werr)) def save_font(self, level: StripLevel) -> None: - cdef wchar_t werr[1024] - cdef char err[1024] - result = self.app_.SaveFont(level, werr) + cdef wchar_t werr[ERR_BUF_SIZE] + cdef char err[ERR_BUF_SIZE] + result = self.app_.SaveFont(level, werr, ERR_BUF_SIZE) if(result != True): raise FileNotFoundError(self.app_.wCharToChar(err, werr)) From 07d0f6cbb0221f09efc588d735e521aabf9363e7 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 7 Dec 2021 15:10:07 -0800 Subject: [PATCH 14/25] Update Cython build & few warnings --- pyproject.toml | 9 ++++++++- setup.py | 6 +++--- src/CvtManager.cpp | 4 ++-- src/File.cpp | 10 ++++++---- src/application.cpp | 3 +-- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b0bbb69..0a0f8e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,4 +4,11 @@ requires = [ "wheel", "cython", ] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" + +[tool.cibuildwheel] +# only build for 64-bit +skip = ["*-manylinux_i686"] + +#test-requires = "pytest" +#test-command = "pushd {project} && pytest" \ No newline at end of file diff --git a/setup.py b/setup.py index 4ceff4e..9650b3d 100644 --- a/setup.py +++ b/setup.py @@ -18,16 +18,16 @@ linetrace = True define_macros.append(('CYTHON_TRACE_NOGIL', '1')) -extra_compile_args = [''] +extra_compile_args = [] if platform.system() != 'Windows': - extra_compile_args.append('-std=c++1y') + extra_compile_args.append('-std=c++14') extension = Extension( 'vttcompilepy.vttcompilepy', define_macros=define_macros, include_dirs=[".","src"], - sources=['vttcompilepy/vttcompilepy.pyx','src/application.cpp','src/CvtManager.cpp','src/File.cpp','src/List.cpp', 'src/mathutils.cpp', 'src/Memory.cpp','src/Platform.cpp','src/TextBuffer.cpp', 'src/TMTParser.cpp', 'src/TTAssembler.cpp', 'src/TTEngine.cpp', 'src/TTFont.cpp', 'src/TTGenerator.cpp', 'src/Variation.cpp', 'src/VariationInstance.cpp', 'src/VariationModels.cpp' ], + sources=['vttcompilepy/vttcompilepy.pyx','src/application.cpp','src/CvtManager.cpp','src/File.cpp','src/List.cpp', 'src/MathUtils.cpp', 'src/Memory.cpp','src/Platform.cpp','src/TextBuffer.cpp', 'src/TMTParser.cpp', 'src/TTAssembler.cpp', 'src/TTEngine.cpp', 'src/TTFont.cpp', 'src/TTGenerator.cpp', 'src/Variation.cpp', 'src/VariationInstance.cpp', 'src/VariationModels.cpp' ], language='c++', extra_compile_args=extra_compile_args, ) diff --git a/src/CvtManager.cpp b/src/CvtManager.cpp index b225c37..dd202b7 100644 --- a/src/CvtManager.cpp +++ b/src/CvtManager.cpp @@ -1672,10 +1672,10 @@ void PrivateControlValueTable::SortCvtKeys(int32_t low, int32_t high) { // quick bool PrivateControlValueTable::DumpControlValueTable(TextBuffer *text) { int32_t cvtNum,pos; - bool newFormat; + //bool newFormat; wchar_t dump[maxLineSize],groupStrg[32],colorStrg[32],directionStrg[32],categoryStrg[32],relativeStrg[32]; - newFormat = this->IsControlProgramFormat(); + //newFormat = this->IsControlProgramFormat(); for (cvtNum = this->LowestCvtNum(); cvtNum <= this->HighestCvtNum(); cvtNum++) { short cvtValue; if (this->GetCvtValue(cvtNum,&cvtValue)) { diff --git a/src/File.cpp b/src/File.cpp index a01c766..053c657 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -73,10 +73,11 @@ void File::ReadBytes(int32_t numBytes, void *buffer) { // DWORD cb = 0; // m_error = !ReadFile(m_hfile, buffer, numBytes, &cb, NULL); //} - size_t size = 0; + //size_t size = 0; if (m_hfile != nullptr) { - size = fread(buffer, 1, numBytes, (FILE*)m_hfile); + //size = + fread(buffer, 1, numBytes, (FILE*)m_hfile); } } // File::ReadBytes @@ -153,10 +154,11 @@ void File::WriteBytes(int32_t numBytes, void *buffer) // DWORD cb = 0; // m_error = !WriteFile(m_hfile, buffer, numBytes, &cb, NULL); //} - size_t size = 0; + //size_t size = 0; if (m_hfile != nullptr) { - size = fwrite(buffer, 1, numBytes, (FILE*)m_hfile); + //size = + fwrite(buffer, 1, numBytes, (FILE*)m_hfile); } } // File::WriteBytes diff --git a/src/application.cpp b/src/application.cpp index c6156d4..e72c215 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -342,7 +342,7 @@ bool Application::CompileGlyphRange(unsigned short g1, unsigned short g2, bool q bool Application::CompileAll(bool quiet, wchar_t errMsg[], size_t errMsgLen) { - int32_t glyphIndex, binSize, fromGlyph, fromChar, numGlyphs = this->font->NumberOfGlyphs(); + int32_t glyphIndex, binSize, numGlyphs = this->font->NumberOfGlyphs(); bool done; unsigned char* binData; wchar_t tempErrMsg[maxLineSize], compErrMsg[maxLineSize]; @@ -427,7 +427,6 @@ bool Application::CompileAll(bool quiet, wchar_t errMsg[], size_t errMsgLen) } } - fromGlyph = this->glyphIndex; fromChar = this->charCode; for (glyphIndex = 0; glyphIndex < numGlyphs && done; glyphIndex++) { if (!quiet && ((glyphIndex + 1) % 10 == 0)) wprintf_s(L"."); if (!quiet && ((glyphIndex + 1) % 200 == 0)) wprintf_s(L"\n"); From 83c161a7d3759528551d900b1094b99e64bed58c Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 7 Dec 2021 16:29:57 -0800 Subject: [PATCH 15/25] Linux GCC warnings --- src/TTAssembler.cpp | 8 ++++---- src/TTEngine.cpp | 4 ++-- src/TTFont.cpp | 13 +++++++------ src/TTGenerator.cpp | 18 ++++++++++++++---- src/TextBuffer.cpp | 3 --- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/TTAssembler.cpp b/src/TTAssembler.cpp index 9139576..29eb358 100644 --- a/src/TTAssembler.cpp +++ b/src/TTAssembler.cpp @@ -1284,10 +1284,10 @@ short TT_DeltaLevel( unsigned short opCode ) void TT_CompileDelta( tt_deltaPType dArr[], short count, unsigned short insCode, short args[], short *argCount); void TT_CompileDelta( tt_deltaPType dArr[], short deltaCount, unsigned short insCode, short args[], short *argCount ) { - short i, tmp, sCount, valid; + short i, tmp, sCount; //, valid; unsigned char argCode; - valid = 1; + //valid = 1; sCount = 0; for ( i = 0; i < deltaCount; i++) { @@ -2466,7 +2466,7 @@ wchar_t *TT_InnerCompile( short *argStore, *aPtr; unsigned char *insStore, *iPtr; short NeedTwoPass = false; /* used to be MyCode */ - short ghtblock ; + //short ghtblock ; short loop; short args[256], argc, argc2; wchar_t args2[256]; @@ -2563,7 +2563,7 @@ wchar_t *TT_InnerCompile( CurrentPtr = StartPtr; LastLineCompiled = 0; - ghtblock = false; + //ghtblock = false; loop = 1; for ( LineNb = 1; CurrentPtr <= EndPtr && *tt_error == tt_NoError; ) { diff --git a/src/TTEngine.cpp b/src/TTEngine.cpp index ce84c68..77f63d2 100644 --- a/src/TTEngine.cpp +++ b/src/TTEngine.cpp @@ -881,7 +881,7 @@ void TTSourceEngine::DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, short amnt,sign,magnitude,shift,size,deltaSubBase,deltas; bool singleStep; wchar_t charFn,buf[128]; - short singlePpemSizes,singlePpemSize[maxPpemSize],ppemRanges,ppemRangeLow[maxPpemSize],ppemRangeHigh[maxPpemSize],offsFn; + short singlePpemSizes,singlePpemSize[maxPpemSize],ppemRanges,ppemRangeLow[maxPpemSize],ppemRangeHigh[maxPpemSize]; //,offsFn; // with a maximum resolution of 1 and a minimum resolution of 1/64, we can delta in range -8..+8 in steps of 1/64. // this means that upto 3 steps have to be performed: doing the entire pixels, doing the 1/8 pixels, and finally @@ -909,7 +909,7 @@ void TTSourceEngine::DLT(bool cvt, DeltaColor color, short knot, F26Dot6 amount, SplitPpemSize(singleStep && color == alwaysDelta ? threshold2UseDeltaFn : 1,ppemSize,&singlePpemSizes,singlePpemSize,&ppemRanges,ppemRangeLow,ppemRangeHigh); if (ppemRanges > 0) { - offsFn = cvt ? offsetCvtFn : 0; + //offsFn = cvt ? offsetCvtFn : 0; for (size = 0; size < ppemRanges; size++) { if (ppemRangeLow[size] == ppemRangeHigh[size]) if (color == alwaysDelta) diff --git a/src/TTFont.cpp b/src/TTFont.cpp index 086eed8..ddc5a55 100644 --- a/src/TTFont.cpp +++ b/src/TTFont.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #define _CRT_SECURE_NO_DEPRECATE +#pragma GCC diagnostic ignored "-Wmultichar" #include // assert #include @@ -2024,7 +2025,7 @@ void PackMaxp(unsigned char *sfnt, sfnt_maxProfileTable *profile) { bool TrueTypeFont::UnpackHeadHheaMaxpHmtx(wchar_t errMsg[], size_t errMsgLen) { sfnt_FontHeader *phead, head; sfnt_HorizontalHeader *phhea, hhea; - unsigned short aw,lsb,*hmtx; // actually hhea->numberOf_LongHorMetrics of sfnt_HorizontalMetrics [aw,lsb] pairs the monospaced part where we have only lsb numbers + unsigned short aw = 0,lsb,*hmtx; // actually hhea->numberOf_LongHorMetrics of sfnt_HorizontalMetrics [aw,lsb] pairs the monospaced part where we have only lsb numbers int32_t glitLen,numEntries,numGlyphs,i,k; unsigned char* pmaxp = this->GetTablePointer(tag_MaxProfile); @@ -2340,7 +2341,7 @@ typedef struct { void TrueTypeFont::GetFmt4(sfnt_mappingTable *map) { // I made no attempt to understand this unsigned short j; int16 i,segCount,*idDelta,delta; - uint16 gid, end, *endCount, start, *startCount, *idRangeOffset, offset, *glyphIdArray; + uint16 gid, end, *endCount, start, *startCount, *idRangeOffset, offset; //, *glyphIdArray; sfnt_cmap4hdr *cmap4hdr = (sfnt_cmap4hdr*)(&map[1]); segCount = SWAPW(cmap4hdr->segCountX2) >> 1; @@ -2348,7 +2349,7 @@ void TrueTypeFont::GetFmt4(sfnt_mappingTable *map) { // I made no attempt to und startCount = endCount + (segCount + 1); idDelta = (int16 *)&startCount[segCount]; idRangeOffset = (uint16 *)&idDelta[segCount]; - glyphIdArray = &idRangeOffset[segCount]; + //glyphIdArray = &idRangeOffset[segCount]; for (i = 0; i < segCount && endCount[i] != 0xFFFF; i++) { @@ -4639,7 +4640,8 @@ uint32_t TrueTypeFont::PackGlyph(unsigned char *dst, int32_t glyphIndex, TrueTyp uint32_t size; short i, numberOfPoints, x, y, delta, j, whoseMetrics = 0; unsigned char bitFlags; - bool composite,useMyMetrics; + //bool composite = false; + bool useMyMetrics = false; size = 0; @@ -4664,12 +4666,11 @@ uint32_t TrueTypeFont::PackGlyph(unsigned char *dst, int32_t glyphIndex, TrueTyp pStart = dst; - composite = useMyMetrics = false; if ( glyph->componentSize > 0 ) { int32_t len = 0; short * flags = nullptr; - composite = true; + //composite = true; WRITEALIGNEDWORD( dst, SWAPW(glyph->ComponentVersionNumber) ); diff --git a/src/TTGenerator.cpp b/src/TTGenerator.cpp index 6b5d104..b2d358f 100644 --- a/src/TTGenerator.cpp +++ b/src/TTGenerator.cpp @@ -1826,15 +1826,25 @@ void TTSourceGenerator::Serif(bool forward, short type, short knots, short knot[ void TTSourceGenerator::Scoop(short parent0, short child, short parent1, wchar_t error[], size_t errorLen) { Vector base; bool ok,y = false; - short dist,actualCvt; + short actualCvt; + //short dist; base = SubV(this->V[parent1],this->V[parent0]); base.x = Abs(base.x); base.y = Abs(base.y); ok = true; - if (base.y <= base.x*this->tanF) { dist = (short)base.y; y = true; } // near horizontal - else if (base.x <= base.y*this->tanF) { dist = (short)base.x; y = false; } // near vertical - else { + if (base.y <= base.x*this->tanF) + { + //dist = (short)base.y; + y = true; + } // near horizontal + else if (base.x <= base.y*this->tanF) + { + //dist = (short)base.x; + y = false; + } // near vertical + else + { ok = false; swprintf(error,errorLen,L"cannot accept SCOOP (base differs from horizontal or vertical by %f degrees or more)",(double)strokeFudge); } diff --git a/src/TextBuffer.cpp b/src/TextBuffer.cpp index 9b08842..7b8b393 100644 --- a/src/TextBuffer.cpp +++ b/src/TextBuffer.cpp @@ -16,9 +16,6 @@ #include #include "TextBuffer.h" -#ifndef _WIN32 -#define swprintf(wcs, ...) swprintf(wcs, 1024, __VA_ARGS__) -#endif #ifndef Min #define Min(a,b) ((a) < (b) ? (a) : (b)) From fdf1b56a5af898443d3c120e300790237e0c77c0 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 7 Dec 2021 17:51:22 -0800 Subject: [PATCH 16/25] Update README.md and pypi.md --- README.md | 32 +++++++++++++++++++++++++------- pypi.md | 3 ++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 583043a..ccd98bd 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,22 @@ For details on the tool visit https://docs.microsoft.com/en-us/typography/tools/ Visual TrueType compiles binary from source formats included in the fonts that are instructed with Visual TrueType. -This repo contains source for VTTCompile which is a standalone tool can compile the high level VTT Talk source -to a lower level assembler source and then can assemble that source to cooresponding binaries. Since VTT can also -generate variation 'cvar' for variable fonts, VTTCompile can also compile the 'cvar' from source format. VTTCompile -also includes file management functions to prepare a font for production and to strip source formats for a final -'ship' font binary. +This repo contains the source for the compilers of Visual TrueType. The compilers can compile the high level VTT Talk source +to a lower level assembler source and then can assemble that source to corresponding binaries. Since VTT can also generate +variation 'cvar' data for variable fonts, the compilers can also produce 'cvar' data from a source format. File management +functions are included to prepare a font for production and to strip source formats for a final 'ship' font binary. -The interface for VTTCompile is similar to VTTShell which is included in Visual TrueType download package. +The source in this repo can be compiled and used in two different ways. + +First is VTTCompile which is a standalone tool can VTTCompile that runs on the command line. The interface for VTTCompile is +similar to VTTShell which is included in Visual TrueType download package. + +Second is VTTCompilePy which is Cython based Python extension that exports interfaces enabling a Python program to compile +and assemble TrueType data. This extension also provides a command line interface to the Python interface. VTTCompilePy is +available on PyPi, please visit https://pypi.org/project/vttcompilepy. The source code in the repo is mostly a subset of the source code of Visual TrueType as needed to produce VTTCompile. -However not all of the source code included in the repo is necessary to produce VTTCompile but we optomized for including +However not all of the source code included in the repo is necessary to produce VTTCompile but we optimized for including as complete source files as possible to make future maintenance easier. For documentation of the Visual TrueType source formats visit https://docs.microsoft.com/en-us/typography/tools/vtt/tsi-tables. @@ -32,6 +38,18 @@ Build as 32-bit on 64-bit systems with `cd src && make CXXFLAGS=-m32`. You may need to do `cd src && make CXXFLAGS="-std=c++14"` to explicitly request support for the 2014 ISO C++ standard. +In the "vttcompilepy" folder is the Cython source for the Python extension but the build is +done through the setup.py file in the main folder. + +Setup a Python environment including dependencies in requirements-dev.txt. + +To build the extension on local machine use "Python setup.py build". +To install the built extension into current Python environment use "Python setup.py install". +To create a distribution package for current system use "Python setup.py bdist_wheel". + +The workflow Python Extension uses ciBuildWheel to build the extension across multiple platforms +and optionally upload result to PyPi. + The minimum compiler requirement is support for the 2014 ISO C++ standard plus amendments. ## Contributing diff --git a/pypi.md b/pypi.md index cbe954d..2a87078 100644 --- a/pypi.md +++ b/pypi.md @@ -3,7 +3,8 @@ VTTCompilePy is a Python extension built using Cython. It provides streamlined bindings for various compilers from Visual TrueType. -DWriteShapePy was developed to support a Python based font development environment. +VTTCompilePy was developed to support a Python based font development environment. In addition to the Python interface, +a command line interface is also installed. Usage is available with "vttcompilepy --help". ### Example From 64fa055864ef491626d688c984899196647d0239 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 8 Dec 2021 12:06:42 -0800 Subject: [PATCH 17/25] Remove vttcompile.h, commend GCC pragma since it does not work anyway. --- src/TTFont.cpp | 2 +- src/vttcompile.h | 35 --------------------------- vttcompile/vttcompile.vcxproj | 1 - vttcompile/vttcompile.vcxproj.filters | 3 --- 4 files changed, 1 insertion(+), 40 deletions(-) diff --git a/src/TTFont.cpp b/src/TTFont.cpp index ddc5a55..c2ae079 100644 --- a/src/TTFont.cpp +++ b/src/TTFont.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #define _CRT_SECURE_NO_DEPRECATE -#pragma GCC diagnostic ignored "-Wmultichar" +//#pragma GCC diagnostic ignored "-Wmultichar" #include // assert #include diff --git a/src/vttcompile.h b/src/vttcompile.h index 515ca14..f82a9e6 100644 --- a/src/vttcompile.h +++ b/src/vttcompile.h @@ -2,39 +2,4 @@ // Licensed under the MIT License. #pragma once -class Application -{ -public: - Application(void); - bool Create(); - virtual ~Application(void); - bool OpenFont(std::string fileName, wchar_t errMsg[]); - bool SaveFont(StripCommand strip, wchar_t errMsg[]); - bool SaveFont(std::string fileName, StripCommand strip, wchar_t errMsg[]); - - bool GotoFont(wchar_t errMsg[]); - bool GotoGlyph(int32_t code, bool isGlyphIndex); - - bool CompileTalk(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]); - bool CompileCommon(int32_t* errPos, int32_t* errLen, wchar_t errMsg[]); - bool CompileGlyphRange(unsigned short g1, unsigned short g2, int32_t* errPos, int32_t* errLen, bool quiet, wchar_t errMsg[]); - bool CompileAll(int32_t* errPos, int32_t* errLen, bool quiet, wchar_t errMsg[]); - - bool BuildFont(StripCommand strip, wchar_t errMsg[]); - -private: - - std::unique_ptr glyf = nullptr, prep = nullptr, talk = nullptr, fpgm = nullptr, cpgm = nullptr; - short platformID = 3, encodingID = 1; - - wchar_t mainFont[maxLineSize], textFont[maxLineSize]; - - bool fontOpen = false; - std::unique_ptr font = nullptr; - std::unique_ptr glyph = nullptr; - std::string fileName; - int32_t charCode = 0, glyphIndex = 0; - - -}; diff --git a/vttcompile/vttcompile.vcxproj b/vttcompile/vttcompile.vcxproj index 1c65767..7eafc74 100644 --- a/vttcompile/vttcompile.vcxproj +++ b/vttcompile/vttcompile.vcxproj @@ -182,7 +182,6 @@ - diff --git a/vttcompile/vttcompile.vcxproj.filters b/vttcompile/vttcompile.vcxproj.filters index f281670..fdc557d 100644 --- a/vttcompile/vttcompile.vcxproj.filters +++ b/vttcompile/vttcompile.vcxproj.filters @@ -128,8 +128,5 @@ Header Files - - Header Files - \ No newline at end of file From ab9e7d9e01940f5e81ad55339b2facac9fba49bb Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 22 Dec 2021 15:13:15 -0800 Subject: [PATCH 18/25] Basic tests --- pyproject.toml | 28 +++++----- requirements-dev.txt | 14 +++-- setup.py | 5 +- src/TMTParser.cpp | 1 + src/TTFont.h | 28 +++++----- tests/data/Selawik-variable.ttf | Bin 481464 -> 483552 bytes tests/test_vttcompile.py | 94 +++++++++++++++++++++++++++++--- vttcompilepy/vttcompilepy.pyx | 4 +- 8 files changed, 128 insertions(+), 46 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0a0f8e4..62f6e46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,14 @@ -[build-system] -requires = [ - "setuptools", - "wheel", - "cython", -] -build-backend = "setuptools.build_meta" - -[tool.cibuildwheel] -# only build for 64-bit -skip = ["*-manylinux_i686"] - -#test-requires = "pytest" -#test-command = "pushd {project} && pytest" \ No newline at end of file +[build-system] +requires = [ + "setuptools", + "wheel", + "cython", +] +build-backend = "setuptools.build_meta" + +[tool.cibuildwheel] +# only build for 64-bit +skip = ["*-manylinux_i686"] + +test-requires = "pytest" +test-command = "pytest {project}/tests" \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 71a0ec9..45318f6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,8 @@ -cython>=0.29.1 - -wheel>=0.31 - -pytest - +cython>=0.29.1 + +wheel>=0.31 + +pytest + +fonttools + diff --git a/setup.py b/setup.py index 9650b3d..25d91de 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,10 @@ if platform.system() != 'Windows': extra_compile_args.append('-std=c++14') - + +if platform.system() == 'Windows': + extra_compile_args.append('-sdl') + extension = Extension( 'vttcompilepy.vttcompilepy', define_macros=define_macros, diff --git a/src/TMTParser.cpp b/src/TMTParser.cpp index 930842e..31e7243 100644 --- a/src/TMTParser.cpp +++ b/src/TMTParser.cpp @@ -375,6 +375,7 @@ void AdjustFPs(short serifType, FormParam *formParams) { void TMTSourceParser::Parse(bool *changedSrc, int32_t *errPos, int32_t *errLen, wchar_t errMsg[], size_t errMsgLen) { Symbol cmd; ActParam aParam; + memset(&aParam, 0, sizeof(ActParam)); short formParamNum; FormParam formParams[maxFPs]; int32_t cmdStart,actParam; diff --git a/src/TTFont.h b/src/TTFont.h index c82bd5e..a67141a 100644 --- a/src/TTFont.h +++ b/src/TTFont.h @@ -240,30 +240,30 @@ class TrueTypeGlyph { // serves mainly as an interface to new user interface TrueTypeGlyph(void); virtual ~TrueTypeGlyph(void); LinkColor TheColor(short from, short to); // such that everybody can use this, not only the compiler - bool Misoriented(short contour); + // bool Misoriented(short contour); short xmin, ymin, xmax, ymax; // bounding box; xmin corresponds to left side bearing short realLeftSideBearing, realRightSideBearing, blackBodyWidth; // as obtained from specifying left and right point by GrabHereInX, to do with auto-hinter??? // contour, knot data - int32_t numContoursInGlyph; - short startPoint[MAXCONTOURS]; - short endPoint[MAXCONTOURS]; + int32_t numContoursInGlyph = 0; + short startPoint[MAXCONTOURS] = {0}; + short endPoint[MAXCONTOURS] = {0}; - short x[MAXPOINTS]; // these seem to be the (coordinates of the) control points - short y[MAXPOINTS]; // Use start|endPoint arrays for contour identification - bool onCurve[MAXPOINTS]; // on curve? - F26Dot6 xx[MAXPOINTS]; // used to get coordinates back from the rasterizer - F26Dot6 yy[MAXPOINTS]; + short x[MAXPOINTS] = {0}; // these seem to be the (coordinates of the) control points + short y[MAXPOINTS] = {0}; // Use start|endPoint arrays for contour identification + bool onCurve[MAXPOINTS] = {0}; // on curve? + F26Dot6 xx[MAXPOINTS] = {0}; // used to get coordinates back from the rasterizer + F26Dot6 yy[MAXPOINTS] = {0}; // composite - bool composite,useMyMetrics; - short componentData[MAXCOMPONENTSIZE]; // binary of TT composite - short componentSize; // size of above data - short ComponentVersionNumber; // sort of a magic number, tends to be -1, I'd prefer this to disappear + bool composite = false,useMyMetrics = false; + short componentData[MAXCOMPONENTSIZE] = {0}; // binary of TT composite + short componentSize = 0; // size of above data + short ComponentVersionNumber = 0; // sort of a magic number, tends to be -1, I'd prefer this to disappear TrueTypeBluePrint bluePrint; private: - short dirChange[MAXPOINTS]; // used during TheColor + short dirChange[MAXPOINTS] = {0}; // used during TheColor }; #define CO_CompInstrFollow 1 diff --git a/tests/data/Selawik-variable.ttf b/tests/data/Selawik-variable.ttf index db26a3f4f8b50b102f1e1e7f89f9ef770abb9cf9..96c37a9111fdbdb7d81e617acc41d39d41fe1a3b 100644 GIT binary patch delta 916 zcmZ`&Ye-XJ7=FHQJ7=33t~Bj7o1`^O(#&F1khf_U#cFCsg*)lof~F^TN|Q)avLHlh ziv1```jJA>Wx)#ivFeXvf|*@@xS{W zhDI;|23ph@&d;^vok|=v0PHzXBZ?301d9$bEbB`y9A9* zrU9~aNUB|wmV6E3E{elc;gaN&$J_LTZS=s=6%^m{gi*X)p?G%1p+?0R=Z}pi;ugL2 z`zRqT69Az^B`&+nl8 zDR=4s#6SjMSDbdKGQ)Mw3{6fi<*#uPk-Q+`0pW4N3C?ntQeSmoa}qSUYCxFYUsEBK zb#B-T&1S+X?vm9 zam>Tq3v_)X+LwVq;ML@qIQcgjYc?AfB*vN(;e8!{g?aBpbj;}< z{w&t%I9)_oFo;NR=hD0Te3mbw+nz8phY5=>rVZ7VN3NfOj*gM!p(XBq$d65~zTx@A z%xc~Rc&_mTkDsf(qx`Ah^(^5kPZBqJOFFLVy#7c1Qno{w%QB17-@BIj)zE21F5DYCQ=I<<%wERA%=W zIDHSG`f7LN=H~^^)dE~O?ON}sDygc=wduhby~KN}9G5IJdr#jHUkBn%#Jlr$9~G{E zJE?I73>Y#(DMrBf1z_VIsk_lPAA9^BnN0$T4?QH< z(T0;r@PPz@M$s=pz!AC;HSzey=Gz|jySEa^9s-&|jg-Ib&z%OpWd+;;uUl-e+}x<3 z&FKI*?WKjBv@Du)QGJ%z7wGb|RhWrWfttPM?pBvrGj#qIaCDGP#QDT7Ni&;TO$jCv z7+OT1+q^vR3K*;ccus2d2kr>^v%upTAa+b@cS~H%_isSvWzxw1o|9=Ze+5>HoC!tS zh>7|FbGD2(QT(*f_cn5MZzg+QHAnQIym&!4A!{oIqg)VVPBk|S$U3WFkPTKrt5~eU zf<|%G@(0vPgUAmiDvNc(6Hb|M^5aZVR|p!F(h=Z8O^Ufvc&>{ldyeiaHWEQX?-Em% z7`xHaHymWtIqE89IH>l$&LC@g&dAO_{@KUf|JY}+X%vHvZ;9gorMQOQ;beOauaygH z`RtVFhvje>NzYJB(&yH) Date: Wed, 22 Dec 2021 15:35:56 -0800 Subject: [PATCH 19/25] Test dependancies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 62f6e46..7486c5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,5 +10,5 @@ build-backend = "setuptools.build_meta" # only build for 64-bit skip = ["*-manylinux_i686"] -test-requires = "pytest" +test-requires = ["pytest", "fonttools"] test-command = "pytest {project}/tests" \ No newline at end of file From d4b5be0eea8a52a08380abb214a71a876ed0664d Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 22 Dec 2021 19:04:40 -0800 Subject: [PATCH 20/25] Return missing emHeight value from VTT. --- src/TTFont.cpp | 5 +++++ src/TTFont.h | 1 + src/TTGenerator.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/TTFont.cpp b/src/TTFont.cpp index c2ae079..ea9722b 100644 --- a/src/TTFont.cpp +++ b/src/TTFont.cpp @@ -1047,6 +1047,11 @@ unsigned char* TrueTypeFont::GlyfBin(void) { return this->binData[asmGLYF]; } +void TrueTypeFont::GetHeights(int32_t* emHeight) +{ + *emHeight = this->unitsPerEm; +} // TrueTypeFont::GetHeights + bool TrueTypeFont::GetHMTXEntry(int32_t glyphIndex, int32_t *leftSideBearing, int32_t *advanceWidth) { sfnt_HorizontalMetrics *horMetric; diff --git a/src/TTFont.h b/src/TTFont.h index a67141a..7a70e29 100644 --- a/src/TTFont.h +++ b/src/TTFont.h @@ -311,6 +311,7 @@ class TrueTypeFont { bool GetGlyph(int32_t glyphIndex, TrueTypeGlyph *glyph, wchar_t errMsg[], size_t errMsgLen); int32_t GlyfBinSize(void); unsigned char* GlyfBin(void); + void GetHeights(int32_t* emHeight); bool GetHMTXEntry(int32_t glyphIndex, int32_t *leftSideBearing, int32_t *advanceWidth); int32_t NumberOfGlyphs(void); int32_t GlyphIndexOf(uint32_t charCode); diff --git a/src/TTGenerator.cpp b/src/TTGenerator.cpp index b2d358f..bbec866 100644 --- a/src/TTGenerator.cpp +++ b/src/TTGenerator.cpp @@ -1997,6 +1997,8 @@ void TTSourceGenerator::InitTTGenerator(TrueTypeFont *font, TrueTypeGlyph *glyph this->glyph = glyph; this->glyphIndex = glyphIndex; this->charCode = this->font->CharCodeOf(this->glyphIndex); + this->font->GetHeights(&this->emHeight); + this->charGroup = this->font->CharGroupOf(this->glyphIndex); this->knots = (glyph->numContoursInGlyph ? glyph->endPoint[glyph->numContoursInGlyph - 1] + 1 : 0) + PHANTOMPOINTS; From 5c39b4c962285da76f2b81984cedf22954d6bc02 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 4 Jan 2022 14:44:27 -0800 Subject: [PATCH 21/25] Update pyproject.toml Skip pp* tags. --- pyproject.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7486c5d..c496a5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,7 @@ requires = [ build-backend = "setuptools.build_meta" [tool.cibuildwheel] -# only build for 64-bit -skip = ["*-manylinux_i686"] +skip = ["*-manylinux_i686", "pp*"] test-requires = ["pytest", "fonttools"] -test-command = "pytest {project}/tests" \ No newline at end of file +test-command = "pytest {project}/tests" From 4637ac5fa3486f61d4c9e7742c03e465e4fae472 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 4 Jan 2022 17:21:44 -0800 Subject: [PATCH 22/25] Update py_ext_main.yml Add deploy job --- .github/workflows/py_ext_main.yml | 54 ++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/.github/workflows/py_ext_main.yml b/.github/workflows/py_ext_main.yml index 7defacc..ad2efbd 100644 --- a/.github/workflows/py_ext_main.yml +++ b/.github/workflows/py_ext_main.yml @@ -4,14 +4,13 @@ on: # Triggers the workflow on push or pull request events but only for the main branch push: branches: [ main ] + tags: ["v*.*.*"] pull_request: branches: [ main ] - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: + build: # The type of runner that the job will run on runs-on: ${{ matrix.os }} @@ -32,11 +31,56 @@ jobs: uses: actions/setup-python@v2 with: python-version: "3.x" + - name: Install dependencies run: pip install cibuildwheel + - name: Build Wheels run: python -m cibuildwheel --output-dir wheelhouse - - uses: actions/upload-artifact@v2 + + - name: Upload Wheels + uses: actions/upload-artifact@v2 with: name: vttcompilepy-${{ matrix.os }} - path: wheelhouse/*.whl + path: ./wheelhouse/*.whl + + - name: Build Source Distribution + run: python setup.py sdist + + - name: Upload Source Distribution + uses: actions/upload-artifact@v2 + with: + path: ./dist/*.zip + + deploy: + + needs: [build] + # only run if the commit is tagged... + if: startsWith(github.ref, 'refs/tags/v') + # ... and all build jobs completed successfully + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade setuptools cython wheel twine + - name: Download artifacts from build jobs + uses: actions/download-artifact@v2 + with: + name: artifact + path: dist/ + + - uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.pypi_password }} + + + From 3f7f0fb32f49b6d980a472254b6e36532b88d0e0 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Tue, 4 Jan 2022 17:47:20 -0800 Subject: [PATCH 23/25] Update py_ext_main.yml add cython --- .github/workflows/py_ext_main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/py_ext_main.yml b/.github/workflows/py_ext_main.yml index ad2efbd..705e96f 100644 --- a/.github/workflows/py_ext_main.yml +++ b/.github/workflows/py_ext_main.yml @@ -33,7 +33,10 @@ jobs: python-version: "3.x" - name: Install dependencies - run: pip install cibuildwheel + run: | + python -m pip install --upgrade pip + pip install cibuildwheel + pip install cython - name: Build Wheels run: python -m cibuildwheel --output-dir wheelhouse From 630cad5d6fc0331b7c2d472409d7ba2ff20ccacf Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 5 Jan 2022 13:45:39 -0800 Subject: [PATCH 24/25] Update setup.py version = '0.0.1.2' --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 25d91de..61dc345 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ setup( name="vttcompilepy", - version= '0.0.1.1b1', + version= '0.0.1.2', description="Python extension for Visual TrueType font compile. ", long_description=long_description, long_description_content_type='text/markdown', From f9383ff34039f8488ff8ea0dc186b866c1667eb4 Mon Sep 17 00:00:00 2001 From: Paul Linnerud Date: Wed, 5 Jan 2022 14:09:50 -0800 Subject: [PATCH 25/25] Update py_ext_main.yml --- .github/workflows/py_ext_main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/py_ext_main.yml b/.github/workflows/py_ext_main.yml index 705e96f..51e6e7b 100644 --- a/.github/workflows/py_ext_main.yml +++ b/.github/workflows/py_ext_main.yml @@ -77,7 +77,6 @@ jobs: - name: Download artifacts from build jobs uses: actions/download-artifact@v2 with: - name: artifact path: dist/ - uses: pypa/gh-action-pypi-publish@master