[HN Gopher] Text rendering hates you (2019)
___________________________________________________________________
Text rendering hates you (2019)
Author : andsoitis
Score : 185 points
Date : 2025-12-22 00:14 UTC (6 days ago)
(HTM) web link (faultlore.com)
(TXT) w3m dump (faultlore.com)
| gnabgib wrote:
| (2019) Popular in:
|
| 2023 (290 points, 119 comments)
| https://news.ycombinator.com/item?id=36478892
|
| 2022 (399 points, 154 comments)
| https://news.ycombinator.com/item?id=30330144
|
| 2019 (542 points, 170 comments)
| https://news.ycombinator.com/item?id=21105625
| jesse__ wrote:
| The ligatures part of this article gets me every time I re-read
| it. I think reading this article may have been the first time I
| realized that even large, well-funded projects are still done by
| people who are just regular humans, and sometimes settle for
| something that's good enough.
| thot_experiment wrote:
| I've tried to ask this before in various contexts and I've never
| been able to find an answer but maybe commenters on a post like
| this would know.
|
| I like the way that the CJK fonts render without anti-aliasing on
| windows. I want to know why and how to cause windows to render a
| non-cjk font of my choosing in this aliased style. I am not
| opposed to hex-editing or otherwise modifying the font if that's
| necessary. I've never been able to find information bout the
| mechanism or how it's triggered.
| Permik wrote:
| Just disable ClearType and all your text will be uniform :)
| thot_experiment wrote:
| Well that's not what I want, I want to specifically prevent
| some passages in text from being rendered as anti-aliased for
| art reasons.
| bawolff wrote:
| At some point, if you're doing it for art reasons, it makes
| the most sense to just render to an image.
| thot_experiment wrote:
| right, i can solve this okay by rendering an image and
| then putting transparent text over it in order to
| preserve editability, but it's such a pain in the ass,
| and i know windows is capable of doing it because it
| _does_ do it, i 'm not looking for a solution, i want to
| understand a facet of windows font rendering
| mgdlbp wrote:
| https://int10h.org/blog/2016/01/windows-cleartype-truetype-f...
|
| http://www.electronicdissonance.com/2010/01/raster-fonts-in-...
| thot_experiment wrote:
| !!!! thank you !!!!
| socalgal2 wrote:
| And the companion article: https://lord.io/text-editing-hates-
| you-too/
|
| (posted in other other threads too)
| xg15 wrote:
| > _Don't ask about the code which line-breaks partial ligatures
| though._
|
| Wondered about this. All the circular dependencies sound like you
| could feasibly get some style/layout combinations that lead to
| self-contradictory situations.
|
| E.g. consider a ligature that's wider than the characters'
| individual glyphs. If the ligature is at the end of the box, it
| could trigger a line break. But that line break would also break
| up the ligature and cause the characters to be rendered as
| individual glyphs, reducing their width - which would undo the
| line break. But without the line break, the ligature would
| reconnect, increase the width and restore the line break, etc
| etc...
| bfgeek wrote:
| Blink's (Chromium) text layout engine works the following way.
|
| 1. Layout the entire paragraph of text as a single line.
|
| 2. If this doesn't fit into the available width, bisect to the
| nearest line-break opportunity which might fit.
|
| 3. Reshape the text up until this line-break opportunity.
|
| 4. If it fits great! If not goto 2.
|
| This converges as it always steps backwards, and avoids the
| contradictory situations.
|
| Harfbuzz also provides points along the section of text which
| is safe to reuse, so reshaping typically involes only a small
| portion of text at the end of the line, if any.
| https://github.com/harfbuzz/harfbuzz/issues/224
|
| This approach is different to how many text layout engines
| approach this problem e.g. by adding "one word at a time" to
| the line, and checking at each stage if it fits.
| nicoburns wrote:
| > This approach is different to how many text layout engines
| approach this problem e.g. by adding "one word at a time" to
| the line, and checking at each stage if it fits.
|
| Do you know why Chrome does it this way?
| bfgeek wrote:
| We found it was roughly on par performance wise for simple
| text (latin), and faster for more complex scripts (thai,
| hindi, etc). It also is more correct when there is kerning
| across spaces, hyphenation, etc.
|
| For the word-by-word approach to be performant you need a
| cache for each word you encounter. The shape-by-paragraph
| approach we found was faster for cold-start (e.g. the first
| time you visit a webpage). But this is also more difficult
| to show in standard benchmarks as benchmarks typically
| reuse the same renderer process.
| Karliss wrote:
| Few more additional ones, more about editing than just rendering:
|
| The style change mid ligature has a related problem. While it
| might be reasonable not to support style change in the middle of
| ligature, you still want to select individual letters within
| ligatures like "ff", "ffi" and "fl". The problem just like with
| color change is that neither the text shaper nor program
| rendering text knows where each individual letter within ligature
| glyph is positioned. Font simply lacks this information.
|
| From what I have seen most programs which support it use similar
| approximation as what Firefox uses for coloring - split the
| ligature into equal parts. Works good enough for something like
| "fi", "fl" not so much for some of ligatures within programming
| fonts that combine >= into >=.
|
| There are even worse edge cases in scripts for other languages.
| There are ligatures which look roughly like the 2 characters
| which formed it side by side but in reverse order. There are also
| some ligatures in CJK fonts which combine 4 characters in a
| square.
|
| Backspace erases characters at finer granularity than it's
| possible to select them.
|
| With regards to LTR/RTL selection weirdness I recently discovered
| that some editors display small flag on the cursor displaying
| current position direction when it's in mixed direction text.
| tomcam wrote:
| I cannot imagine a use case where I would want to do a style
| change mid ligature. Can someone smarter than I am give a
| reasonable example of doing so?
| jfengel wrote:
| The user may not think of the letters as connected. Suppose
| the user wanted to write "stuffing" and bold the letters
| "ing". The user may well not realize that the font thinks of
| "ffi" as anything other than three separate letters.
| tomcam wrote:
| Excellent example! Thanks.
| Karliss wrote:
| Ligatures like in "stuffing" isn't the worst case for mid
| ligature styling. You could introduce a split between stuff
| and ing preventing the forming of ligature and it would
| likely still look reasonable. That's actually one of the
| most straight forward things you can do for text layout,
| split text into runs with same style and then shape each
| run separately. That's also how you end up with mess shown
| in Safari screenshot. In non English scripts where
| ligatures are less optional things are trickier. Not
| applying the ligature can significantly affect the look. In
| some fonts/scripts where ligatures are used for diacritic
| marks or syllable based combinations of characters into
| single glyph.
|
| Another aspect of mid ligature color changes is that if you
| allow color you probably allow any other style change
| including font size or the font itself, which in turn can
| have completely different size and shaped glyph for the
| corresponding ligature and even different set of ligatures.
| Thus making drawing of corresponding characters as single
| ligature impossible.
|
| One of the most warranted and also one of the trickiest
| cases for wanting mid ligature style change is language
| education materials. You might want to highlight individual
| subcomponents of complex character combinations to explain
| rules behind them. For these cases the firefox splitting
| hack is not good enough. Although it seems like in current
| version of Firefox on Linux nhRe is handling much better
| than in 2019 screenshot. This might be as much as
| improvement in Firefox and underlying libraries as it was
| in font. At the end of day if font draws a complex
| character combination as single shape there is nothing font
| rendering software can do to correctly split into logical
| components. Instead of ligatures you can draw such
| characters as multiple overlapping and appropriately placed
| glyphs (possibly in combination with context aware
| substitutions). Kind of like zalgo text, no font has
| separate glyphs for each letter with every combination of
| 20 stacked diacritic marks. That way the information about
| components isn't lost making it technically possible to
| correctly style each of them, but it's still not easy.
| gudzpoz wrote:
| > some editors display small flag on the cursor displaying
| current position direction
|
| I was amazed to see IDEA/RustRover doing exactly this [1] when
| I added BIDI texts to my code to test things out.
|
| [1] https://i.imgur.com/Qqlyqpc.png (image taken from IDEA
| issue tracker)
| littlestymaar wrote:
| > Few more additional ones, more about editing than just
| rendering
|
| Right after TFA was published, someone put together the text
| editing version: Text editing hates you too:
| https://lord.io/text-editing-hates-you-too/
| cmyr wrote:
| A technical note: OpenType Layout does have a way of
| representing the appropriate _cursor positions_ to use for
| components of a ligature[1], which is a good proxy for where
| the individual glyph boundaries are in the trivial case (fi and
| fl, say) but these tables are not reliably included in all
| fonts, and they are not actually used by much client software
| (last I checked they were used by CoreText but not by HarfBuzz
| or DirectWrite.)
|
| [1]: https://learn.microsoft.com/en-
| us/typography/opentype/spec/g...
| casey2 wrote:
| The real takeaway from the article is that you can rathole
| forever on ill-defined problems. Decide upfront whether you care
| about actual humans and their usecases or hypothetical humans and
| their hypothetical usecases.
| PKop wrote:
| Or even, which subset of humans' uses cases you wish to concern
| yourself with as you can't always please everyone or tackle
| everyone's problems. If one only cared about a single language
| everything becomes much easier.
| nicoburns wrote:
| > If one only cared about a single language everything
| becomes much easier.
|
| Yes. Let's be thankful that isn't the case for browsers and
| major GUI toolkits though.
| tankenmate wrote:
| Hmm I use Firefox and the rendering I see in Firefox looks
| nothing like the render the author gets in Firefox; in fact the
| text rendering I get looks very similar to the "Chrome"
| rendering. Obviously this must depend on the libraries linked
| during the build process.
| kg wrote:
| Depending on your OS Firefox will select from multiple
| rendering backends based on your GPU, driver etc.
|
| On Windows it may or may not be using DirectWrite for text
| rasterization as a general thing, and in some cases text might
| be rasterized using a different fallback path if DirectWrite
| can't handle the font, I think.
|
| IIRC this was/is true for Chrome as well, where in some cases
| it software rasterizes text using Skia instead of calling
| through to the OS's font implementation.
| nicoburns wrote:
| IIRC, Chrome now uses CoreText/DirectWrite for system fonts
| on macOS/Windows, and Skrifa (FreeType rewritten in Rust)
| outlines rasterized with Skia for everything else (system
| fonts on Linux, web fonts on all platforms).
|
| I believe Firefox leans on the system raserizers a little
| more heavily (using them for everything they support), and
| also still uses FreeType on Linux.
| Denvercoder9 wrote:
| The article is from 2019, things might also simply have changed
| since then.
| lovich wrote:
| How did they get the exact effect to show what they want in the
| text here instead of say, me seeing the exact same visuals for
| each browser as I am reading it from a single browser?
| zerocrates wrote:
| You mean in the parts that say "Here's what they look like in
| Safari" and so on? Those are just .pngs.
| lovich wrote:
| I missed some UI improvement in browsers then as I can copy
| and paste them as text, and even the italic emoji example
| carried over the italic information when I tried copying it
| into various editors.
| namibj wrote:
| It's just transparent text over png background.
| charcircuit wrote:
| >But if the transform is an animation this will actually look
| even worse
|
| I wish they provided an example video of this since I can't
| visualize it. My natural thinking is subpixel antialiasing should
| look fine.
|
| >the characters will jiggle as each glyph bounces around between
| different subpixel snappings and hints on each frame.
|
| This shouldn't be a big issue unless your animation is slow and
| your subpixels are big.
| akdor1154 wrote:
| The issue (i think) is that the animation is done post-
| rasterizing. So a translate of integer pixels is fine, but
| scale? Skew? Suddenly you have really visible colour fringing
| appearing out of nowhere.
| charcircuit wrote:
| >the animation is done post-rasterizing.
|
| The article is talking about "rerasterize the glyphs in their
| new location", which means it's rasterizing post animation. I
| think he's implying that there is something unstable with his
| each pixel is treated that breaks the illusion.
| djaouen wrote:
| Good. I hated _it_ first!
| shmerl wrote:
| _> So subpixel-AA is a really neat hack that can significantly
| improve text legibility, great! But, sadly, it's also a huge pain
| in the neck!_
|
| Especially when you have a monitor with unusual subpixel layout,
| which is very common for OLEDs that don't have any standard for
| it. In practice, developers of common font libraries like
| FreeType simply didn't bother with trying to support all that.
| And that trickles down to toolkits like Qt. Surprising the
| article doesn't mention this major problem with modern displays.
|
| _> Retina displays really don't need it_
|
| Assuming this means high resolution displays - unfortunately
| that's not always what you end up using. So subpixel antialiasing
| can still be useful, if it can work. But as above, it's often
| just broken on OLEDs.
| namibj wrote:
| Arguably monitors that are not mere TVs ought to allow control
| of each distinct pixel they drive internally and communicate
| their layout and if needed distinct brightness/color
| coordinates to the host.
|
| Exceptions can apply if the consumers of the screen can't
| resolve details finer than "emulated sRGB pixels" anyways.
| shmerl wrote:
| Something like that should be done in EDIDs may be, but you
| still would need to support a ton of different layouts in the
| end. LCD monitors are a lot more limited in that sense.
| Dwedit wrote:
| "Subpixel offsets break glyph caches"
|
| I once resolved that by keeping a vertically shrunken but really
| wide glyph around in a cache. Just resample it for a different
| horizontal offset.
| mananaysiempre wrote:
| The AGG ("Anti-Grain Geometry") library does something
| similar[1], from what I understand.
|
| Also, I had (though never tested) the impression that in the
| Windows world ClearType uses 3x the horizontal resolution
| internally (I vaguely remember that being mentioned in the
| horror novel^W^W _Raster Tragedy_ [2] somewhere?..). Given many
| font designers' testing process for their hinting bytecode
| seems to be to run it through ClearType and check if it looks
| OK (not unlike firmware programmers...), we all, including
| Microsoft, are essentially stuck with that choice forever (or
| at least until people with painfully low-res displays become
| rare enough that the complaining about blurry text can be
| disregarded). So I'd expect 1/3 of a pixel to be the natural
| resolution for a glyph cache, not 1/4? Or have things changed
| in the transition from GDI to GDI+ to DirectWrite?
|
| [1]
| https://agg.sourceforge.net/antigrain.com/research/font_rast...
|
| [2] http://rastertragedy.com/
| tomcam wrote:
| > Text is complicated
|
| So true!
|
| > and english is bad at expressing these nuances.
|
| I think English is a terrible shitpile of grammar and syntax. I'm
| very impressed that anyone who speaks another language natively
| can get good at it.
|
| But I'm interested in the notion that it lacks nuance to describe
| the intricacies of text rendering. Can someone tell me where that
| would apply?
| globalnode wrote:
| As a native english speaker, i did try to learn german but
| eventually gave up. A language sprinkled with "learn by wrote"
| gender prefixes for every item is just not worth learning. I
| did have an issue with the numbers being back to front once you
| get to the unit value but then someone pointed out english does
| that too for the values 13-19... so there ya go.
| MangoToupe wrote:
| > A language sprinkled with "learn by wrote" gender prefixes
| for every item is just not worth learning.
|
| Bantu languages, which cover much of subsaharan Africa, have
| many noun classes ("genders")--sometimes as many as 20. You
| have to learn all sorts of prefixes for each noun class
| depending on their grammatical role in tying to the noun.
|
| However, it's really not so bad. Once you get the hang of the
| noun classes, it actually makes picking up the ear for it
| _faster_. Of course this is more true the more consistent the
| language in applying its internal rules.
| Angostura wrote:
| Learn by 'rote'.
| tenacious_tuna wrote:
| > the notion that it lacks nuance to describe the intricacies
| of text rendering
|
| I took this to mean that any non-domain-specific language may
| be bad at describing that domain, e.g. why physicists,
| mathematicians, chemists, etc. have a common symbology for the
| discipline, or why programming languages exist. i.e., not so
| much that English is uniquely bad among written human language
| for conveying these topics, but just that any non-specialized
| language may be.
|
| Though, I think the author did a fair job, but I lack the
| domain experience to guess at where the misconceptions might
| lie.
| tomcam wrote:
| I had much the same conclusions. The author did a perfectly
| good job of explaining the issues.
| efilife wrote:
| > I'm very impressed that anyone who speaks another language
| natively can get good at it.
|
| From my completely anecdotal observations, native speakers are
| the worst at English. They struggle with homophones,
| prepositions, tenses, confuse meanings of words, apostrophes
| and I could go on and on.
|
| English grammar is easier to learn by reading and writing than
| speaking, what most native speakers do.
|
| Its/it's, they/their/they're, who's/whose, prepositions like a
| lot, a while and confused words like definitely and defiantly
| are the first that come to mind. See if you are better than a
| foreigner.
| InsideOutSanta wrote:
| As an example of this, native German speakers are often
| better at knowing when to use "who" vs "whom" because German
| grammar rules are in some ways a superset of English grammar
| rules.
| aleph_minus_one wrote:
| You don't have to refer to German grammar since the English
| grammar in this case contains all necessary ingredients;
| there _is_ inflection depending on the case:
|
| You don't say "You give _I_ the apple. ", but "You give
| _me_ the apple. " (similar for he, she, we, they), i.e. the
| pronoun is inflected depending on whether it is subject or
| object, so English speakers are perfectly aware on the
| difference between subject and object.
|
| When you refer to the subject, you use "who" and when you
| refer to the object, you use "whom".
| marky1991 wrote:
| Whom is on its way out anyway; I don't think I ever use it
| at all and certainly never hear anyone else use it.
| RedShift1 wrote:
| > I think English is a terrible shitpile of grammar and syntax
|
| Spoken languages are like programming languages, there are the
| ones people complain about and the ones nobody uses.
| rhdunn wrote:
| And start with simpler regular rules and get more complex
| over time as words are imported and reimported,
| pronunciations shift, grammatical rules morph and evolve
| (often to simplify grammatical genders and cases) while
| leaving their mark, and spelling changes.
|
| For example, goose/geese is the result of the plural form and
| singular form undergoing different paths in the Great Vowel
| Shift resulting in the different vowels in the modern form.
|
| There's also evidence that Proto-Indo-European had laryngeal
| consonants that have disappeared in all modern languages
| derived from it [1], but have left their mark on the
| descendant languages.
|
| [1] https://en.wikipedia.org/wiki/Laryngeal_theory
| cenamus wrote:
| Then there are also the lovely instances of deliberate
| misspellings / insertion of letters into words that never
| had them in English.
|
| Eg. receipt, which has the p only in Latin, but had long
| lost it by the time Old French brought it to Britain.
| kibwen wrote:
| Don't get me started on _colour_ , caused by the English
| aristocracy distorting the Latin/Old French _color_ just
| to look fancy.
| lewisjoe wrote:
| This. A language that doesn't adapt (accumulate shitpile of
| baggage from other languages over changing times) will be a
| dead language eventually.
|
| English will always have my respect for being open/inclusive
| and adaptive.
|
| Interesting fact: If you are looking for a spoken language
| with the cleanest/composable grammar - it's Sanskrit. The
| panini grammar is actually like a programming language where
| sentences are just compositions of lower level similar units.
|
| But like I said it's practically dead (not used as a spoken
| language). But interestingly used as a proxy language for
| translation and other nlp tasks due to it's clean grammar :)
| mmooss wrote:
| > who really cares if "ae" is written as "ae"?
|
| Nitpicking, but if you're writing about text rendering you should
| know:
|
| Yes, ligatures are really about presentation and not semantics.
| For example _fi_ (U+FB01) means the same thing as _fi_ ; it just
| looks neater in some situations.
|
| _ae_ (U+00E6) is not a ligature; it 's a mostly obsolete
| character, with different semantics (or phonetics) than _ae_.
|
| For example, for purely typsetting beauty, your word processor
| might substitute the ligature _fi_ for the two letters _fi_
| (which can f* search, and I resent both the ligature and lazy
| search function developers). It would never substitute _ae_ for
| _ae_ ; that would misspell the word as much as substituting an
| _o_.
| froh wrote:
| > _lazy_ search function developers
|
| doing non-ascii first needs awareness and then quickly becomes
| tricky (encodings yay).
|
| getting combining characters and/or homoglyphs right is hard.
|
| and if you're still bored out: have fun with Unicode
| confusables.txt ...
|
| with this in mind I dare to give them lazy bums the honor of
| the doubt and rather call them something between naive and
| scared.
| mmooss wrote:
| ok, fine. :)
|
| Isn't there a library out there for this common set of
| problems? I know Unicode provides normalization tables,
| though I don't know how good they are and I don't know if
| Unicode also provides a library.
| oeitho wrote:
| > ae (U+00E6) is not a ligature; it's a mostly obsolete
| character, with different semantics (or phonetics) than ae.
|
| Reading that a letter in my alphabet is mostly obsolete feels
| really weird. No rebuttal, just a comment.
|
| > It would never substitute ae for ae; that would misspell the
| word as much as substituting an o.
|
| While that is correct, a lot of other systems actually do this
| exact substition. If your name contains _ae_ it will be
| substituted with _ae_ in passports, plane tickets and random
| other systems throughout your life.
|
| My own username on this website is an example of a similar
| substition. The _oe_ should be read as the single character
| _o_.
| glimshe wrote:
| Languages often simplify as they evolve, dropping "annoying"
| characters like ae. In fact, it was replaced by "e" (or ae
| itself) in most cases as the words got imported by other
| languages.
| mmooss wrote:
| A personal hypothesis is that additional characters were
| much simpler in the age of handwriting, most of the history
| of literacy, compared to the age of print, the current age.
|
| Using handwriting, additional characters are simple and in
| fact Medieval European scribes used many abbreviations,
| etc. When you need to set type on a printing press, or even
| input a character not already on your computer keyboard,
| the barrier is higher.
| kzrdude wrote:
| I hope that the implication is that ae is obsolete in
| English. Because it is used in English!
| mmooss wrote:
| It's mostly obsolete in English, which I think is safe to
| say and which does not conflict with it being used. For
| example, I think few people know how to type it into a
| computer, while everyone who uses a Latin alphabet can type
| _ae_.
| mmooss wrote:
| > Reading that a letter in my alphabet is mostly obsolete
| feels really weird. No rebuttal, just a comment.
|
| Sorry, I should have specified 'in English'.
|
| > a lot of other systems actually do this exact substition.
| If your name contains ae it will be substituted with ae
|
| I agree and to clarify, I meant that the reverse substitution
| doesn't happen.
| oeitho wrote:
| > I agree and to clarify, I meant that the reverse
| substitution doesn't happen.
|
| Re-reading your comment, yeah its obvious that that was
| what you meant. My apologies, that's on me.
| mmooss wrote:
| No problem at all!
| Sesse__ wrote:
| > mostly obsolete
|
| The Nordic languages beg to differ!
| Snild wrote:
| Keep Swedish out of this, you dirty Danes!
|
| Edit: Checked out your profile, correcting myself: "you silly
| north-Danes!"
| mmooss wrote:
| Yes, sorry, I should have said, 'in English'.
| gudzpoz wrote:
| Nitpicking your nitpicking: I think the author meant better.
|
| The "ae" example was used as an introductory example for us
| _English readers_. Unlike the Arabic examples where ligatures
| are mandatory and supported by most Arabic fonts, not many
| English fonts have an "ae" ligature these days. Not to mention
| this is a web page and a user can freely apply their !important
| font styles.
|
| Using ae to mean "treat it as an 'ae' rendered by ligature
| which is visually indistinguishable" does not mean the author
| knows nothing about this (although the wording can use some
| improvement to reduce the ambiguity).
| mmooss wrote:
| I don't understand: _ae_ is _not_ a ligature so it 's not an
| example of a ligature. There are English ligatures to use.
|
| Also, most fonts have many characters beyond ASCII, including
| _ae_. If your font lacked it then you would see an empty box,
| not the two letters _ae_. Applying a font style would not
| change the rendering of _ae_ into ASCII letters; I don 't
| think it changes the rendering of English ligatures, which
| are separate code points in Unicode.
| djoldman wrote:
| > Just so you have an idea for how a typical text-rendering
| pipeline works, here's a quick sketch:
|
| 1. Styling (parse markup, query system for fonts)
|
| 2. Layout (break text into lines)
|
| 3. Shaping (compute the glyphs in a line and their positions)
|
| 4. Rasterization (rasterize needed glyphs into an atlas/cache)
|
| 5. Composition (copy glyphs from the atlas to their desired
| positions)
|
| Why is layout done so early? It seems to me that that would be
| later in the process.
| gudzpoz wrote:
| An example: the text "Hello There l bsm llh Beep Boop!!" should
| turn into two visual lines as follows if it is line-wrapped:
| Hello There l bsm llh Beep Boop!!
|
| Notice how "l" goes from the fifth word (from left to right) to
| the third after the line wrap. This won't work if shaping
| happens before line wrapping.
| djoldman wrote:
| Is this the case if all the text is in Arabic? Or if all the
| text was in Arabic, could layout be done after shaping?
| syngrog66 wrote:
| After seeing "english" twice in one paragraph I stopped reading.
___________________________________________________________________
(page generated 2025-12-28 23:01 UTC)