textbox salvando no banco mesmo estando vazia

Question: Validação e mensagem:
 if (txtFone.Text.Trim() == string.Empty)
 {
      MessageBox.Show("O campo Telefone é obrigatório!");
      txtFone.Focus();
      return false;
 }

Código do botão gravar:
 if (this.Valida())
 {
            Model.cliente_fisico cliente_fisico = new model.cliente_fisico();
            BLL.cliente_fisico bllCliente_fisico = new BLL.cliente_fisico();
            cliente_fisico.idClientefisico = Convert.ToInt32(lblid.Text);
            cliente_fisico.nome = txtNome.Text;
            cliente_fisico.rg = txtRG.Text;
            cliente_fisico.cpf = txtCPF.Text;
            cliente_fisico.email = txtEmail.Text;
            cliente_fisico.endereco = txtEndereco.Text;
            cliente_fisico.nr = txtNr.Text;
            cliente_fisico.cep = txtCEP.Text;
            cliente_fisico.estado = txtEstado.Text;
            cliente_fisico.telefone = txtFone.Text;
            cliente_fisico.cidade = txtCidade.Text;
            if (cliente_fisico.idClientefisico == -1)
                bllCliente_fisico.Insert(cliente_fisico);
            else bllCliente_fisico.Update(cliente_fisico);
            dgvClienteFisico.DataSource = bllCliente_fisico.Select();
  }

Answer: O que acontece é que o trecho abaixo nunca será verdade, pois por padrão a mascara é aplicada.
    if (txtFone.Text.Trim() == string.Empty) {

        MessageBox.Show("O campo Telefone é obrigatório!");
        txtFone.Focus();
        return false;

    }

Então, você tem duas opções, modificar a propriedade TextMaskFormat para remover a máscara, selecionando a opção ExcludePromptAndLiterals ou você remove os caracteres da máscara na hora da verificação.
Trocando a propriedade:

Mudando a verificação:
if (String.IsNullOrWhiteSpace(txtFone.Text.Replace("-", string.Empty)))
{
        MessageBox.Show("O campo Telefone é obrigatório!");
        txtFone.Focus();
        return false;
}

Lembre-se: Você deve substituir todos os caracteres da máscara para validar se o usuário informou alguma coisa.
Comment: Deu certo, apareceu a mensagem. Só que agora incluiu sem os pontos e o traço
Comment: Qual o seu padrão de máscara? Altere a sua pergunta e coloque o padrão que eu modifico a resposta para poder salvar com a máscara
Answer: Use este condicional funciona directinho
//Inicia o metodo aqui
void VerificarCampos(){

if(txtFone.Text == "")
     {
       MessageBox.Show("O campo Telefone é obrigatório ", "Informação",       MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
txtFone.Focus();
return false;
}
return true;
}//aqui enceras o metodo

//no final chama este metodo antes do select do botao guardar

if (!VerificarCampos()) return;
 Model.cliente_fisico cliente_fisico = new model.cliente_fisico();
            BLL.cliente_fisico bllCliente_fisico = new BLL.cliente_fisico();
            cliente_fisico.idClientefisico = Convert.ToInt32(lblid.Text);
            cliente_fisico.nome = txtNome.Text;
            cliente_fisico.rg = txtRG.Text;
            cliente_fisico.cpf = txtCPF.Text;
            cliente_fisico.email = txtEmail.Text;
            cliente_fisico.endereco = txtEndereco.Text;
            cliente_fisico.nr = txtNr.Text;
            cliente_fisico.cep = txtCEP.Text;
            cliente_fisico.estado = txtEstado.Text;
            cliente_fisico.telefone = txtFone.Text;
            cliente_fisico.cidade = txtCidade.Text;
            if (cliente_fisico.idClientefisico == -1)
                bllCliente_fisico.Insert(cliente_fisico);
            else bllCliente_fisico.Update(cliente_fisico);
            dgvClienteFisico.DataSource = bllCliente_fisico.Select();
        }

Comment: incluiu o campo de caracteres da mascara ainda ..


Plot a decision tree with R

Question: I have a 440*2 matrix that looks like:
1   144
1   152
1   135
2   3
2   12
2   107
2   31
3   4
3   147
3   0
4   end
4   0
4   0
5   6
5   7
5   10
5   9

The left column are the starting points eg in the app all the 1's on the left would be on the same page. They lead to three choices, pages 144,152,135. These pages can each then lead to another page, and so on until the right hand column says 'end'. What I would like is a way to visualise the scale of this tree. I realise it will be quite large given the nb of rows so maybe not graph friendly, so for clarity I want to know how many possible routes there are in total (from every start point, down every option it gives and the end destinations of each. I realise there will be overlaps but thats why I am finding this hard to calculate).
secondly, each number has an associated title.  I would like to have a function whereby if you input a given title it will plot all the possible starting points and their associated paths that will lead there. This should be a lot smaller and therefore graph friendly.
e.g.
dta <- "
14  12  as
186  187  Frac
187  154  Low
23   52   Med
52   11   Lip
15  55  asd
11   42   AAA
42   154   BBB
154   end  Coll"

Edited example data to show that some branches are not connected to desired tree
  dta <- "
  14  12  as
  186  187  Frac
  187  154  Low
  23   52   Med
  52   11   Lip
  11   42   AAA
  42   154   BBB
  154   end  Coll"

 dta <- gsub("   ", ",", dta, fixed = TRUE)
 dta <- gsub("  ", ",", dta, fixed = TRUE)

df <- read.csv(textConnection(dta), stringsAsFactors = FALSE, header = FALSE)
names(df) <- c("from", "to", "nme")
library(data.tree)
Warning message:
package 'data.tree' was built under R version 3.2.5
tree <- FromDataFrameNetwork(df)
 **Error in FromDataFrameNetwork(df) :**
  **Cannot find root name. network is not a tree!**

I made this example to show how column 1 leads to a value in column 2 which then refers to a value in column 1 until you reach the end. Different starting points can ultimately lead to different length paths to same destination. so this would look sometigng like:

So here, I wanted to see how you could go from all start points to 'Coll'
greatly appreciate any help
Comment: I don't have an answer for you, but maybe [this](http://blog.revolutionanalytics.com/2013/06/plotting-classification-and-regression-trees-with-plotrpart.html) will help.
Comment: thanks Tim. But doesn't rpart classify the data instead of plotting it as is? or if not, how do I get it to just plot the tree as I described above?
Comment: prp() in rpart.plot can give you some ideas. Look for code inside function.
Answer: If you have indeed a tree (e.g. no cycles), you can use data.tree:
Start by converting to a data.frame:
 dta <- "
14  12  as
186  187  Frac
187  154  Low
23   52   Med
52   11   Lip
15  55  asd
11   42   AAA
42   154   BBB
154   end  Coll
55  end  efg
12  end  hij"

dta <- gsub("   ", ",", dta, fixed = TRUE)
dta <- gsub("  ", ",", dta, fixed = TRUE)

df <- read.csv(textConnection(dta), stringsAsFactors = FALSE, header = FALSE)
names(df) <- c("from", "to", "nme")

Now, convert to a data.tree:
library(data.tree)
tree <- FromDataFrameNetwork(df)

tree$leafCount

You can now navigate to any sub-tree, for analysis and plotting. E.g. using any of the following possibilities:
subTree <- tree$FindNode(187)
subTree <- Climb(tree, nme = "Coll", nme = "Low")
subTree <- tree$`154`$`187`

subTree <- Clone(tree$`154`)

Maybe printing is all you need:
print(subTree , "nme")

This will print like so:
  levelName          nme
1 154                Coll
2  ¦--187             Low
3  ¦   °--186        Frac
4  °--42              BBB
5      °--11          AAA
6          °--52      Lip
7              °--23  Med

Otherwise, use fancy plotting:
SetNodeStyle(subTree , style = "filled,rounded", shape = "box", fontname = "helvetica", label = function(node) node$nme, tooltip = "name")
plot(subTree , direction = "descend")

This looks like this:
Comment: this looks great. but the example I gave is an oversimplification. The real data is more like the first set where not all of the rows lead to Coll. How would you just plot the Coll relevant rows?
Comment: You can navigate to any sub-tree, e.g. `subTree <- tree$FindNode(187)`  or `subTree <- Climb(tree, nme = "Coll", nme = "Low")`
Comment: Im sorry, Im not used to this sort of work so my terminology is probably off. Ive tried to clarify the example. I can't navigate to a subtree because the original dataframe has rows that do not lead to coll and hence are not connected to the desired tree. This means that I get the error code Ive added to post about it not being a Tree
Comment: Your example is still a tree, but you're missing the ref from 12 and 55 to `end`. I updated the answer accordingly.
Comment: ok but this is hardcoded now. I want to have as input just 'Coll' and the dataframe and for the code to create the tree instead of having to manually write 187-->154 etc as I will be doing it lots of times for different Colls.
Comment: If you can send me your data, I can have a look (send it to the email at the data.tree maintainer.
Comment: Thanks for all the help Christoph, with your code and a lot of tinkering I fixed the problem in the end.
Comment: Christoph, Ive run into some memory issues with this. If I have labels that are too long (>4) in a 30*3 data.frame it crashes. Any ideas how I can fix this withotu having to reduce the length of the labels so much theyre uninformative.

Plot a decision tree with R


How to stop webform from sending an email

Question: I'm using Drupal 7.12 / Webform 3
How do I stop email from being sent upon a webform submission? I want to do this conditionally so I need to use a hook. I still want everything else to go through correctly (i.e. confirmation page, db entry for submission).
I see that there are these lines in function webform_client_form_submit($form, &$form_state)
if (!$is_draft && !$form_state['values']['details']['finished']) {
    $submission = webform_get_submission($node->webform['nid'], $sid, TRUE);
    webform_submission_send_mail($node, $submission);
  }

How do I hook into that and stop it?
Answer: If you go the end of the function webform_submission_send_mail() inside the file webform.submissions.inc (inside weform module's include directory), you'll notice that the webform module using the function drupal_mail() to send the mail.
So a better place to change the behavior of the mail from being sent (alter in Drupal terms) is hook_mail_alter(), which is used to alter an email message created by using drupal_mail() function.
Implementation of the hook to stop the e-mails:
function MODULE_mail_alter(&$message) {
  //You can do your conditional check here and depending on that
  //you can set the value of $message['send'] to false to stop the mail from being sent.
  //place the line below inside conditional loop, or else it'll stop every mail from being sent ;-)
  $message['send'] = FALSE;
}

Note: This hook will only work if the mail is generated by drupal_mail(). If you're sending mail by some other menthod, it'll not be invoked. But, by default Webform module uses drupal_mail() as mentioned earlier, so there's nothing to worry :-)
You'll also need to clear the caches to make the change effective.
Answer: You can also use webform with Rules and Webform Rules. Are your conditions something complicated that Rules cannot implement?

This module adds rules integration on webforms.
Answer: You should just remove all recipients on here:
yoursite.com/node/XXX/webform/emails

when there are no recipients no emails will be sent.
Comment: Ok, then you should write your own submit function, in fact you can copy and paste the code of webform_client_form_submit and just modify it to your needs. And then in your iimplementation of hook_form_alter for the webform you should replace default submit function with yours.
Comment: Yeah. I only want to stop the emails under certain conditions which I will calculate in my custom module. So I still need email addresses in there.
Comment: @Alex Petrov I upvoted your comment, parhaps you should update your answer with this solution
Answer: you even do not need override webform_client_form_submit function, because 'webform_client_form_submit' function works with $form['#node']->webform['emails'] array, which you can modify in form_alter hook. (unset your email for some specific condition). I've tested it on webform 4v, maybe for 3v object can have other structure, but i think it also have to work
Answer: I had to do this in hook_webform_submission_insert.  I just looped through the emails and changed their status. In my case I wanted to enable them so set them to 1.
   foreach ($node->webform['emails'] as $key => $email) {
    $node->webform['emails'][$key]['status'] = 1 // or 0 to disable;
  }

Answer: I used the same approach as Cameron, by using the insert hook.
Setting the status for each email did not work for me however.
I managed to get it to work by unsetting the emails I did not want to go out.
foreach ($node->webform['emails'] as $key => $email) {
  unset($node->webform['emails'][$key]);
}


Is is possible to load php functions from a text file?

Question: I recently updated my server to use SuPHP to increase security.
One downside is that I used to be able to include files from my other domains hosted on my server using absolute paths to the file. This doesn't work anymore.
The file in particular that I include from other domains is my functions file which is huge and used on all my websites. For the sake of maintainability, I've been including a central file rather than uploading the file to each server every time I make changes.
I thought that saving the functions file as plain text and using file_get_contents() across domains might be the right way to go. But apparently this doesn't work.
Is there a way to make it work?
Or is there some other whizz bang function that can help me achieve my goal?
Cheers.
Comment: Are all the websites hosted on the same environment and do you have control over the php.ini?
Comment: A hard link would work across all vhosts/uids.
Comment: Yes, I have php.ini control.
Answer: It sounds like you need configure open_base_dir in each of your websites vhosts so they are allowed to access the directory where these function files reside. You should put them in a common location.
open_basedir
Another alternative would be to use php_info() to check your include path to see if there is a suitable location already configured where you could place these files, then simply include using include()
include_path
Comment: To add to my answer, I would recommend the latter option, since the include path is much better suited to what you are trying to achieve.
Comment: I've checked phpinfo() and next to include_path it says .:/usr/lib/php:/usr/local/lib/php

Is this a suitable location? How would I upload a file at this location?
Comment: You can edit the include_path in ini file to add another path there, seperated by a colon, for example: .:/usr/lib/php:/usr/local/lib/php:/home/user/my_php_files
Comment: If you do not have complete access to the server then this path would have to be a location you do have access to.


Difficulty in setting permanent iptables on Debian

Question: I have problem setting permanent iptables on Debian 6 Squeeze.
I am working on a brand new instance of a server, freshly created, updated and upgraded to latest version.
Here is the tutorial I followed multiple times:
http://wiki.debian.org/iptables
I am doing all operations as root user.
After completing all the steps in the tutorial and rebooting server, the iptables are reset to default state ie. allow everything in and out.
My aim is to set them "for good", so they won't reset every time the system reboots.
I would appreciate any kind of help.
Thanks.
Comment: Are you using iptables-persistent or the scripts described in the wiki? What happens when you run your scripts?
Comment: For the record, I suggest you look at something like firehol, ufw, shorewall, or some other iptables front end.  Creating a really strong set of rules by hand can get pretty difficult.
Comment: I ran `apt-get install iptables-persistent` and followed the steps in the wiki (no, I did not use any scripts). Is there any additional configuration involved in iptables-persistent?
Answer: Ok, first a clarification, did you use the instructions from the Debian Wiki, or did you use the iptables-persistent package?  They operate via different mechanisms.  The Wiki page is basically describing a manually way of accomplishing what iptables-peristent does.
If you installed iptables-persistent, did you put your iptables rules in the file /etc/iptables/rules.v4 (as described in the iptables-persistent documentation)?  If not, that's probably your issue.  Try configuring your iptables rules the way you want them, and then run either:
iptables-save > /etc/iptables/rules.v4

or
/etc/init.d/iptables-persistent save

If you didn't use iptables-persistent, but copied the changes required by the Debian Wiki page, you'll need to show us the modifications you made to the files, the if-pre-up script you added, etc.
UPDATE: The iptables-persistent information above is for the package in wheezy (testing) and sid (unstable).  For squeeze (stable), the file should be /etc/iptables/rules.  Unfortunately, I don't have an easily accessible squeeze/stable based box, so I can't look closer at iptables-persistent on it right now.
Comment: Let's go with the iptables-persistent method. I pasted my rules into `/etc/iptables/rules.v4` and ran `iptables-save > /etc/iptables/rules.v4`. The iptables did not update. I tried to restart iptables-persistent and also do a reboot, but the `iptables -L` stays empty.
Comment: The command `iptables-save > /etc/iptables/rules.v4` would over-write anything you pasted into `/etc/iptables/rules.v4`.  Are you sure you didn't mean to do a restore?
Comment: @Christopher, BTW are you sure the file isn't `/etc/iptables/rules` instead of `rules.v4`?  Is the v4 a wheeze thing perhaps, the `iptables-persistent` on squeeze seems to use a file named `rules`.
Comment: @Zoredache - Good point.  `rules.v4 `is what the wheezy (testing) and sid (unstable) packages are using.  `/etc/iptables/rules` is what the squeeze (stable) package looks to be using.  I'll update the answer info to note the differences.
Comment: I have done the same for `rules` (pasted my rules in there and saved using `iptables-save > /etc/iptables/rules`). Now I rebooted my machine and the changes did not save. Any more ideas? :)


entityFIeldQuery on user table, how can I add a condition for $user->data

Question: I have tried extending EntitiyFieldQuery to add a condition that would be able to match on a $user->data["somevar"], but to no avail. I am not sure this is even possible, as  $user->data is serialised.
I have just the base class working, returing users with status set to 1:
class SubscriberEntityFieldQuery extends EntityFieldQuery {
  // define some defaults for the class
  public function __construct() {
    // now we don't need to define these over and over anymore
    $this
      ->entityCondition('entity_type', 'user')
      ->propertyCondition('status', 1);
  }
}

Would anyone have an idea if this is even possible? Or would I need to resort to using db_query for a case like this?
Answer: The data column contains a serialized PHP string, which MySQL doesn't understand. As far as MySQL is concerned the data is just a string like any other.
Because of that you won't be able to query the properties of the serialized objects in that column without doing some seriously nasty work with SUBSTRING/regex/insert-other-unreliable-method-here.
If possible you should add a field to the user entity instead that represents the same data, and use the EntityFieldQuery with that field. It might not be ideal but it's probably the easiest solution.
Comment: Yes. This was my first impression also. I avoided extra user fields and implemented as a data variable to prevent having extra profile fields for not-often accessed data. Thanks friend.
Answer: May be you need fieldCondition like the one below
class SubscriberEntityFieldQuery extends EntityFieldQuery {
  // define some defaults for the class
  public function __construct() {
    // now we don't need to define these over and over anymore
    $this
      ->entityCondition('entity_type', 'user')
      ->propertyCondition('status', 1)
      ->fieldCondition('field_data_field_yourcustomuserfield', 'value', 'whatever-value-you-are-looking-for', '=')
  }
}

Comment: No, unfortunately this is not in a custom field. The variable is set in $user->data, in a serialised state.
Comment: Oh man variable_get does not match well with fields API. Is there a way you can attach that variable data into a custom field, into the user entity, programatically? [These](http://api.drupal.org/api/drupal/includes%21entity.inc/class/EntityFieldQuery/7?no_cache=1344343850) are all the apis available.
Comment: Custom fields add an extra table to user profile, these add quite a bit more overhead than `$user->data` .  When your profile has a lot of bits of data associated to a profile, you will find `$user->data` to be a nice lightweight alternative.  Unfortunately, you sometimes have to dive through hoops to get to it.
Comment: What you are saying is correct. But since this is user specific task, it needs to live in the fields. If you have 100,000 users then doing variable_get will prove a bit expensive in the long run.
Comment: No, friend. `variable_get()` is not used to get at `$user->data`. It is available in `$user` object after a `user_load()`. Say you have 15 extra data units to associate with a user profile, having 15 extra tables to join on is overkill, so we chose to store the less frequently accessed ones in $user->data, that is somewhat what it is meant to be used for in the first place.
Comment: aah user object, sure. Then the best way would be to wrap your query inside if else statement like this `global $user; if($user->data == compare){query style one} else{query style 2}`
Comment: No, my friend. I am looping through an array of uid's, `global $user` puts the currently logged on user in scope. Hey. I upvoted you for your input though, will post my workaround when I have a few minutes.
Comment: There is one small problem with your code and that is variable `$user` is reserved for user who is logged in. If you are referring to some other account other than the one who is logged in, then you must use `$account` variable. So it should be $account->data. This is specific to drupal only and not php. This concept comes from [Drupal 7 Module Development](http://www.packtpub.com/drupal-7-module-development/book) book.
Comment: I would love to know what solution you could come up with. This is very interesting one.


How to Convert Hijri date to Gregorian and Hijri date to Gregorian vice versa using AngularJs

Question: We have two [Hijiri and English ] datepicker text boxes when given date into hijiri it will convert to Gregorian
Comment: See this simple code here: https://stackoverflow.com/questions/71222556/convert-any-of-the-5-islamic-hijri-calendars-dates-to-any-of-18-world-calenda
Answer: I would recommand you to use a robust solution tested for that.
I'd use moment.js (http://momentjs.com/docs/#/plugins/hijri/) in pair with a Hijiri plugin (https://github.com/xsoh/moment-hijri )
Once you have those plugin; manipulating a date from one side to other is very simple.
See documentation for more info of the hijri plugin : https://github.com/xsoh/moment-hijri


How to display entity label using entity reference field checkbox form display

Question: I am working with Drupal 8. And I want to create a field of entity_reference type. The field has a reference to context_question entity. And the field is displayed fine, but without any description beside the check boxes.
The context_question entity has a label field and I want exactly the label be displayed beside the checkbox in my first entity. What should I change in the code? Or do I need to create the fieldFormatter?
$fields['context_question'] = BaseFieldDefinition::create('entity_reference')
  ->setLabel(t('Context Question'))
  ->setDescription(t('Reference to context-question-type'))
  ->setSetting('target_type', 'context_question')
  ->setDisplayOptions('view', array(
    'label' => 'above',
    'format' => 'label',
    'weight' => 15,
  ))
  ->setDisplayOptions('form', array(
    'type' => 'options_buttons',
    'weight' => 15,
  ))
  ->setCardinality(1000)
  ->setDisplayConfigurable('form', TRUE)
  ->setDisplayConfigurable('view', TRUE);

Answer: I assume the context_question is your custom entity or coming from a contrib module?
There are two options:

Add label key to context_question entity definition under entity_keys so that Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection pick it up. See Aggregator item for reference.
Add a @EntityReferenceSelection plugin. You have to define one. Check TermSelection for how it is implemented for terms. It looks like this:


seek() function?

Question: Please excuse my confusion here but I have read the documentation regarding the seek() function in python (after having to use it) and although it helped me I am still a bit confused on the actual meaning of what it does, any explanations are much appreciated, thank you.
Answer: Regarding seek() there's not too much to worry about.
First of all, it is useful when operating over an open file.
It's important to note that its syntax is as follows:
fp.seek(offset, from_what)

where fp is the file pointer you're working with; offset means how many positions you will move; from_what defines your point of reference:

0: means your reference point is the beginning of the file
1: means your reference point is the current file position
2: means your reference point is the end of the file

if omitted, from_what defaults to 0.
Never forget that when managing files, there'll always be a position inside that file where you are currently working on. When just open, that position is the beginning of the file, but as you work with it, you may advance.
seek will be useful to you when you need to walk along that open file, just as a path you are traveling into.
Comment: +1 for explaining the second parameter, although I'd like to add that you should probably use the constants `os.SEEK_SET`, `os.SEEK_CUR`, and `os.SEEK_END` instead of the magic numbers `0`, `1`, and `2`, respectively.
Comment: Actually in case of append mode, we can seek to file's beginning using fp.seek(0, 0), but as soon as you try to write to that file, seek will again reset to file's end and contents will be written at the end.
Comment: Thanks - learned something new and only had to read this clearly explained answer onces
Comment: The seek position is a byte index into the contents of the file similar to an array index. Its also interesting that if we open file in append mode 'a', we cannot seek to file's beginning.
Comment: when calling seek from the beginning of the file, does it actually start 'seeking' from the beginning of the file each time?
Answer: When you open a file, the system points to the beginning of the file. Any read or write you do will happen from the beginning. A seek() operation moves that pointer to some other part of the file so you can read or write at that place.
So, if you want to read the whole file but skip the first 20 bytes, open the file, seek(20) to move to where you want to start reading, then continue with reading the file.
Or say you want to read every 10th byte, you could write a loop that does seek(9, 1) (moves 9 bytes forward relative to the current positions), read(1) (reads one byte), repeat.
Comment: -1: This answer fails to explain the second parameter, and actually, as phrased, makes it sounds like the second parameter dictates how many bytes are read.
Answer: The seek function expect's an offset in bytes.
Ascii File Example:
So if you have a text file with the following content:
simple.txt
abc

You can jump 1 byte to skip over the first character as following:
fp = open('simple.txt', 'r')
fp.seek(1)
print fp.readline()
>>> bc

Binary file example gathering width :
fp = open('afile.png', 'rb')
fp.seek(16)
print 'width: {0}'.format(struct.unpack('>i', fp.read(4))[0])
print 'height: ', struct.unpack('>i', fp.read(4))[0]

Note: Once you call read you are changing the position of the
  read-head, which act's like seek.
Answer: For strings, forget about using WHENCE: use f.seek(0) to position at beginning of file and f.seek(len(f)+1) to position at the end of file.  Use open(file, "r+") to read/write anywhere in a file. If you use "a+" you'll only be able to write (append) at the end of the file regardless of where you position the cursor.
Comment: Are you talking about files or strings?
Comment: Sorry for the confusion. It's all about files.

seek() function?


Remove row with specific number in R

Question: I want to remove row with the test "student2". However, I don't want to remove row like "student22", "student 23"... etc.
For example:
       Student.Code Values
1  canada.student12      2
2   canada.student2      3 # remove
3  canada.student23      5 # keep
4       US.student2      6 # remove
5     US.student32       2
6    Aus.student87     645
7 Turkey.student25       4 #keep

I used the code grepl("student2", example$Student.code, fixed = TRUE but it also find (remove) the rows with like "student23"
Answer: We can use grepl("student2$", example$Student.Code)
library(tidyverse)
example <- tibble::tribble(
             ~Student.Code, ~Values,
        "canada.student12",      2L,
         "canada.student2",      3L,
        "canada.student23",      5L,
             "US.student2",      6L,
            "US.student32",      2L,
           "Aus.student87",    645L,
        "Turkey.student25",      4L
        )

example$Student.Code
grepl("student2$", example$Student.Code)
[1] FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE

example %>%
  filter(!grepl("student2$", Student.Code))

# A tibble: 5 x 2
  Student.Code     Values
  <chr>             <int>
1 canada.student12      2
2 canada.student23      5
3 US.student32          2
4 Aus.student87       645
5 Turkey.student25      4

Answer: Data:
df <- data.frame(
  Student = c("canada.student12", "canada.student2", "canada.student23","US.student2", "US.student32", "Aus.student87", "Turkey.student25"),
  Value = c(2,3,5,6,2,654,5)
)

Solution: (in base R)
The idea is to use grepl to match those values where the number 2 occurs at the word boundary, that is, in regex, at \\b, and to exclude these strings with the negator !:
df[!grepl("student2\\b", df$Student),]
           Student Value
1 canada.student12     2
3 canada.student23     5
5     US.student32     2
6    Aus.student87   654
7 Turkey.student25     5

Alternatively, you can also go the opposite way and match those patterns that you want to keep:
df[grepl("student(?=\\d{2,})", df$Student, perl = T),]

Here, the idea is to use positive lookahead to match values with student iff they are followed immediately by at least two digits (\\d{2,}). (Note that when using lookahead or lookbehind you need to include perl = T.)
Answer: If you have a variable with an exact value you want to remove, don't use grep or grepl.
example <- tibble::tribble(
             ~Student.Code, ~Values,
        "canada.student12",      2L,
         "canada.student2",      3L,
        "canada.student23",      5L,
             "US.student2",      6L,
            "US.student32",      2L,
           "Aus.student87",    645L,
        "Turkey.student25",      4L
        )

example <- example[example$Student.Code != "canada.student2",]
# or, in dplyr
example <- filter(example, Student.Code != "canada.student2")
# for multiple values
example <- filter(example, !(Student.Code %in% c("canada.student2", "US.student2")))

fixed = TRUE is not working because all it means is 'search for this exact string in the input strings', not 'only match this exact string (it must be the whole value)'

Remove row with specific number in R


connect() failed (111: Connection refused) when trying to reach 1 of 3 apps

Question: I have two Django apps running on my Debian server with Gunicorn.
One running on port 80(app1), the other on 84(app2). app1 is working fine. But when trying to open app2, I get a 502 Bad Gateway from Nginx. Both their configs are almost identical. I have no idea why this isnt working. I also have a third app running but that's running on Flask IIRC.
sites-available/app2
server {
listen 84;
server_name app2;

location / {
    include proxy_params;
    proxy_pass http://127.0.0.1:7000;
}

location /static/ {
    root /home/app1;
    try_files $uri =404;
}
}

sites-available/app1
server {
        listen 80;
        server_name app1;

        location / {
            include proxy_params;
            proxy_pass http://127.0.0.1:8000;
        }

        location /static/ {
            root /home/django;
            try_files $uri =404;
        }
       location /bestanden/ {
            root /home/django/files;
            add_header Pragma public;
            add_header Cache-Control "public";
        }
    }

/etc/systemd/system/app1.service
[Unit]
Description=App1
After=network.target

[Service]
Type=simple
User=root
ExecStart=/bin/bash /home/app1/bin/start-app.sh
Restart=on-failure

[Install]
WantedBy=multi-user.target

/home/app1/bin/start-app1.sh
cd /home/app1/app
echo "Activating"
source /home/app1/env/bin/activate
echo "Starting"
# conf.py contains some environment variables for the app
gunicorn -c "/home/app1/env/bin/gunicorn.conf.py" app1.wsgi

/etc/systemd/system/pricescraper.server
[Unit]
Description=App2
After=network.target

[Service]
Type=simple
User=root
ExecStart=/bin/bash /home/app2/bin/start-app.sh
Restart=on-failure

[Install]
WantedBy=multi-user.target

/home/app2/bin/start-app2
cd /home/app2/app/
source /home/app2/venv/bin/activate
gunicorn -c "/home/app2/venv/bin/gunicorn.conf.py" app2.wsgi -b 192.168.188.43:7000

When I take a look at error.log I get this:
[error] 1560#1560: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.188.199, server: app2, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:7000/", host: "192.168.188.43:84"
I have no idea what this means, googling it ended up on a question where they were using sockets and just removed the ULR prefix, which doesnt apply to me. The weirdest part is when I open 192.168.188.43:7000 I does open the index.html but without any static files loading, which includes necessary JS so I can't test the backend.
Please, any help appreciated!
Answer: Apparently it wasn't supposed to bind to 192.168... but to 127.0.0.1


android:autoSizeTextType in Jetpack Compose

Question: Is there a way to adjust the text to always resize depending a fixed height ?
I have a column that has a fixed height and in which the text inside should always fit
 Column(modifier = Modifier.height(150.dp).padding(8.dp)) {
   Text("My really long long long long long text that needs to be resized to the height of this Column")
}

Answer: I use the following to adjust the font size with respect to the available width:
val textStyleBody1 = MaterialTheme.typography.body1
var textStyle by remember { mutableStateOf(textStyleBody1) }
var readyToDraw by remember { mutableStateOf(false) }
Text(
    text = "long text goes here",
    style = textStyle,
    maxLines = 1,
    softWrap = false,
    modifier = modifier.drawWithContent {
        if (readyToDraw) drawContent()
    },
    onTextLayout = { textLayoutResult ->
        if (textLayoutResult.didOverflowWidth) {
            textStyle = textStyle.copy(fontSize = textStyle.fontSize * 0.9)
        } else {
            readyToDraw = true
        }
    }
)

To adjust the font size based on height, play around with the Text composable's attributes and use didOverflowHeight instead of didOverflowWidth:
val textStyleBody1 = MaterialTheme.typography.body1
var textStyle by remember { mutableStateOf(textStyleBody1) }
var readyToDraw by remember { mutableStateOf(false) }
Text(
    text = "long text goes here",
    style = textStyle,
    overflow = TextOverflow.Clip,
    modifier = modifier.drawWithContent {
        if (readyToDraw) drawContent()
    },
    onTextLayout = { textLayoutResult ->
        if (textLayoutResult.didOverflowHeight) {
            textStyle = textStyle.copy(fontSize = textStyle.fontSize * 0.9)
        } else {
            readyToDraw = true
        }
    }
)

In case you need to synchronize the font size across multiple items in a list, save the text style outside of the composable function:
private val textStyle = mutableStateOf(MaterialTheme.typography.body1)

@Composable
fun YourComposable() {
    Text(...)
}

This is certainly not perfect, as it might take some frames until the size fits and the text draws finally.
Comment: This solution works, but keep in mind that it doesn't work in the preview (Compose beta09). I lost 1 hour trying to figuring out why it was not rendering correctly.

Also, see @zxon response to avoid rendering text size changes.
Comment: @RahulSainani `softWrap = false` might not be necessary in general.
In the example above, I'm scaling down ( `fontSize * 0.9` ), if text is too large. If the text would be allowed to wrap, the layout would grow in height before it would grow in width.
Comment: ```kotlin
Composable calls are not allowed inside the calculation parameter of inline fun  remember(calculation: () -> TypeVariable(T)): TypeVariable(T)
```

```kotlin
val (textStyle, updateTextStyle) = remember { mutableStateOf(MaterialTheme.typography.body1) }
```
Comment: @Forntoh You need to lift the theme reference out of the remember invocation. Seems like this has changed since.
Comment: Thanks for the answer, curious why soft wrap is set to false?
Comment: It gets complicated if you want to handle both cases of having to shrink or expand the text to fit. In the latter case you need to add a step to backtrack after it overflows to the last size that didn't overflow. There's probably a faster way that does something like a binary search rather than multiplying by a fraction that would take only a couple of iterations at most. I'm also experimenting with using the ratio between the font size and `size.width` in `textLayoutResult` but it's not linear so requires some heuristics.
Comment: Also this only works when you want the text to shrink to fit, not when you want it to either shrink or expand like the pre-compose `TextView` `autoSizeTextType` did.
Answer: I built on top of Brian's answer to support other properties of Text which are also hoisted and can be used by the caller.
@Composable
fun AutoResizeText(
    text: String,
    fontSizeRange: FontSizeRange,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    style: TextStyle = LocalTextStyle.current,
) {
    var fontSizeValue by remember { mutableStateOf(fontSizeRange.max.value) }
    var readyToDraw by remember { mutableStateOf(false) }

    Text(
        text = text,
        color = color,
        maxLines = maxLines,
        fontStyle = fontStyle,
        fontWeight = fontWeight,
        fontFamily = fontFamily,
        letterSpacing = letterSpacing,
        textDecoration = textDecoration,
        textAlign = textAlign,
        lineHeight = lineHeight,
        overflow = overflow,
        softWrap = softWrap,
        style = style,
        fontSize = fontSizeValue.sp,
        onTextLayout = {
            Timber.d("onTextLayout")
            if (it.didOverflowHeight && !readyToDraw) {
                Timber.d("Did Overflow height, calculate next font size value")
                val nextFontSizeValue = fontSizeValue - fontSizeRange.step.value
                if (nextFontSizeValue <= fontSizeRange.min.value) {
                    // Reached minimum, set minimum font size and it's readToDraw
                    fontSizeValue = fontSizeRange.min.value
                    readyToDraw = true
                } else {
                    // Text doesn't fit yet and haven't reached minimum text range, keep decreasing
                    fontSizeValue = nextFontSizeValue
                }
            } else {
                // Text fits before reaching the minimum, it's readyToDraw
                readyToDraw = true
            }
        },
        modifier = modifier.drawWithContent { if (readyToDraw) drawContent() }
    )
}

data class FontSizeRange(
    val min: TextUnit,
    val max: TextUnit,
    val step: TextUnit = DEFAULT_TEXT_STEP,
) {
    init {
        require(min < max) { "min should be less than max, $this" }
        require(step.value > 0) { "step should be greater than 0, $this" }
    }

    companion object {
        private val DEFAULT_TEXT_STEP = 1.sp
    }
}

And the usage would look like:
AutoResizeText(
    text = "Your Text",
    maxLines = 3,
    modifier = Modifier.fillMaxWidth(),
    fontSizeRange = FontSizeRange(
        min = 10.sp,
        max = 22.sp,
    ),
    overflow = TextOverflow.Ellipsis,
    style = MaterialTheme.typography.body1,
)

This way I was able to set different maxLines and even have Ellipsis as overflow as the text was just too big to fit into the set lines even with the smallest size we want.
Comment: On very important ting is missing here if the text could be changing, the key for the remember:
`var fontSizeValue by remember(text) { mutableStateOf(fontSizeRange.max.value) }
var readyToDraw by remember(text) { mutableStateOf(false) }`
Comment: This seems to be the most reliable answer. I couldn't get the one that used ParagraphIntrinsics to actually adjust the text size. I was also able to get this to adjust text size back up if needed by making `text` the key for the remember blocks. It's a little slow, but I just adjusted my max text size down a bit to compensate.
Comment: This works well indeed, I'm trying to make it react to layout resizing... it doesn't remeasure the fontSize because readyToDraw is stuck on true, so it won't go into the measuring block. Any suggestions? Thanks
Comment: Ι get `None of the following functions can be called with the arguments supplied.` with latest jetpack compose version :/
Comment: For some reason, the ellipses don't work for me. Do you happen to have a really low maxLines value?
Comment: @F.Mysir make sure you import the correct FontStyle and FontWeight -- I had the same problem as you and the default imports were non-compose.
Comment: This is the best answer so far. Works like a charm. Thank you
Comment: Doesn't work for me at all. Despite that Brian's solution does
Comment: doesn't work correct, it uses less size for text though it can fit higher size for test device
Comment: Good approach, though rather than reimplement `Text` (and all of its parameters) I would write a function just to create the necessary modifier/size/layout parts and pass that to the text. I was able to refactor this approach to the point that using it looks like this: `with(autoScalingText(14.sp..22.sp)) { Text(modifier = textModifier, fontSize = fontSizeState.value, onTextLayout = onTextLayout, ...) }`
Answer: This is a composable based on @Brian and @zxon comments to autosize the Text based on the available width.
@Composable
fun AutoSizeText(
    text: String,
    textStyle: TextStyle,
    modifier: Modifier = Modifier
) {
    var scaledTextStyle by remember { mutableStateOf(textStyle) }
    var readyToDraw by remember { mutableStateOf(false) }

    Text(
            text,
            modifier.drawWithContent {
                if (readyToDraw) {
                    drawContent()
                }
            },
            style = scaledTextStyle,
            softWrap = false,
            onTextLayout = { textLayoutResult ->
                if (textLayoutResult.didOverflowWidth) {
                    scaledTextStyle =
                            scaledTextStyle.copy(fontSize = scaledTextStyle.fontSize * 0.9)
                } else {
                    readyToDraw = true
                }
            }
    )
}

The preview doesn't work correctly with this (at least with beta09), you can add this code to use a placeholder for the preview:
    if (LocalInspectionMode.current) {
        Text(
                text,
                modifier,
                style = textStyle
        )
        return
    }

Comment: @las No, I didn't try that. But I think it may work if you use the `text` as the key of remember:

`var scaledTextStyle by remember(text) { mutableStateOf(textStyle) }
    var readyToDraw by remember(text) { mutableStateOf(false) }`
Comment: Instead using constant fraction `0.9` can use `result.size.width / result.multiParagraph.width`. Its because `didOverflowWidth` implementation compare `size.width` and `multiParagraph.width`. So we can calculate fraction once instead bruteforce values. Also it improves accuracy of final font size and text looks more natural.
Comment: Wrapping this in a @composable is better and easier to use . I am struggling to modify your code to size it back up if the text changes to become shorter and doesn't need to stay small. Have you tried to do it?
Comment: @swap_i I've been experimenting with that but I find the ratio between the font size and size.width is not linear so it might need some fine tuning after the initial guess if it still overflows, or perhaps some heuristics to make it a tiny bit smaller to cover for that possibility in advace.
Answer: I did something like this
@Composable
fun AutosizeText() {

    var multiplier by remember { mutableStateOf(1f) }

    Text(
        "Some long-ish text",
        maxLines = 1, // modify to fit your need
        overflow = TextOverflow.Visible,
        style = LocalTextStyle.current.copy(
            fontSize = LocalTextStyle.current.fontSize * multiplier
        ),
        onTextLayout = {
            if (it.hasVisualOverflow) {
                multiplier *= 0.99f // you can tune this constant
            }
        }
    )
}

you can visually see the text shrinking till it fits
Comment: This one works and is one thing I love about compose: being able to compute values that are static in xml. I used fontSize = size * 0.90f and added a check to make sure it wasn't getting too small (in my case I made sure that the multiplier wasn't < 0.1f.
Comment: The best answer! Other answers work with deffects but this one works as expected.
Comment: I get an error:
java.lang.IllegalArgumentException: Cannot perform operation for Unspecified type.
at androidx.compose.ui.unit.TextUnitKt.checkArithmetic--R2X_6o(TextUnit.kt:344)
Comment: +1 This is the best answer as it uses ``hasVisualOverflow``, allowing it to work on **more than 1 line**
Comment: What if we don't want to see the text shrinking in real time?
Answer: This is based on Robert's solution but it works with maxLines and height constraints.

@Preview
@Composable
fun AutoSizePreview1() {
    Box(Modifier.size(200.dp, 300.dp)) {
        AutoSizeText(text = "This is a bunch of text that will fill the box", maxFontSize = 250.sp, maxLines = 2)
    }
}

@Preview
@Composable
fun AutoSizePreview2() {
    Box(Modifier.size(200.dp, 300.dp)) {
        AutoSizeText(text = "This is a bunch of text that will fill the box", maxFontSize = 25.sp)
    }
}

@Preview
@Composable
fun AutoSizePreview3() {
    Box(Modifier.size(200.dp, 300.dp)) {
        AutoSizeText(text = "This is a bunch of text that will fill the box")
    }
}

@Composable
fun AutoSizeText(
    text: String,
    modifier: Modifier = Modifier,
    acceptableError: Dp = 5.dp,
    maxFontSize: TextUnit = TextUnit.Unspecified,
    color: Color = Color.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    contentAlignment: Alignment? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    maxLines: Int = Int.MAX_VALUE,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
) {
    val alignment: Alignment = contentAlignment ?: when (textAlign) {
        TextAlign.Left -> Alignment.TopStart
        TextAlign.Right -> Alignment.TopEnd
        TextAlign.Center -> Alignment.Center
        TextAlign.Justify -> Alignment.TopCenter
        TextAlign.Start -> Alignment.TopStart
        TextAlign.End -> Alignment.TopEnd
        else -> Alignment.TopStart
    }
    BoxWithConstraints(modifier = modifier, contentAlignment = alignment) {
        var shrunkFontSize = if (maxFontSize.isSpecified) maxFontSize else 100.sp

        val calculateIntrinsics = @Composable {
            val mergedStyle = style.merge(
                TextStyle(
                    color = color,
                    fontSize = shrunkFontSize,
                    fontWeight = fontWeight,
                    textAlign = textAlign,
                    lineHeight = lineHeight,
                    fontFamily = fontFamily,
                    textDecoration = textDecoration,
                    fontStyle = fontStyle,
                    letterSpacing = letterSpacing
                )
            )
            Paragraph(
                text = text,
                style = mergedStyle,
                constraints = Constraints(maxWidth = ceil(LocalDensity.current.run { maxWidth.toPx() }).toInt()),
                density = LocalDensity.current,
                fontFamilyResolver = LocalFontFamilyResolver.current,
                spanStyles = listOf(),
                placeholders = listOf(),
                maxLines = maxLines,
                ellipsis = false
            )
        }

        var intrinsics = calculateIntrinsics()

        val targetWidth = maxWidth - acceptableError / 2f

        check(targetWidth.isFinite || maxFontSize.isSpecified) { "maxFontSize must be specified if the target with isn't finite!" }

        with(LocalDensity.current) {
            // this loop will attempt to quickly find the correct size font by scaling it by the error
            // it only runs if the max font size isn't specified or the font must be smaller
            // minIntrinsicWidth is "The width for text if all soft wrap opportunities were taken."
            if (maxFontSize.isUnspecified || targetWidth < intrinsics.minIntrinsicWidth.toDp())
                while ((targetWidth - intrinsics.minIntrinsicWidth.toDp()).toPx().absoluteValue.toDp() > acceptableError / 2f) {
                    shrunkFontSize *= targetWidth.toPx() / intrinsics.minIntrinsicWidth
                    intrinsics = calculateIntrinsics()
                }
            // checks if the text fits in the bounds and scales it by 90% until it does
            while (intrinsics.didExceedMaxLines || maxHeight < intrinsics.height.toDp() || maxWidth < intrinsics.minIntrinsicWidth.toDp()) {
                shrunkFontSize *= 0.9f
                intrinsics = calculateIntrinsics()
            }
        }

        if (maxFontSize.isSpecified && shrunkFontSize > maxFontSize)
            shrunkFontSize = maxFontSize

        Text(
            text = text,
            color = color,
            fontSize = shrunkFontSize,
            fontStyle = fontStyle,
            fontWeight = fontWeight,
            fontFamily = fontFamily,
            letterSpacing = letterSpacing,
            textDecoration = textDecoration,
            textAlign = textAlign,
            lineHeight = lineHeight,
            onTextLayout = onTextLayout,
            maxLines = maxLines,
            style = style
        )
    }
}

Comment: this is very cool (like all the novel solutions in this thread!). I think it would help to mention what each while loop does though. After messing around with it locally myself I now think I understand it as the first while loop has to be run when `maxLines > 1` (possible optimisation?) so that soft-wrapping of paragraph is adhered to, and the second while loop needs to always be run regardless.
Comment: I updated the code with comments and a bug fix.
Comment: This is better than the higher voted answers since this has no visible delay.
Answer: LAST UPDATE: 11 January 2023 v4.1 performance fine-tuning
Turnkey solution:

Adjusts the text size with optimal performance by using a binary search algorithm to avoid unnecessary iterations.
Very accurate
works with preview and tested on an app with 900k+ downloads.

Features:

Best performance: Utilizes a dichotomous binary search algorithm to quickly find the optimal text size without unnecessary iterations.

Alignment support: Supports 6 possible alignment values through the Alignment interface.

Material Design 3 support.

Font scaling support: Changing the font scale by the user does not affect the visual rendering result.

Multiline Support with maxLines Parameter: AutoSizeText introduces maxLines, enabling control over the maximum number of visible lines for text within a specific space.

Source:
AutoSizeText.kt:
// LAST UPDATE: 11 January 2023 v4.1 performance fine-tuning
package com.inidamleader.ovtracker.util.compose

import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.BoxWithConstraintsScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.InternalFoundationTextApi
import androidx.compose.foundation.text.TextDelegate
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFontFamilyResolver
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.isSpecified
import androidx.compose.ui.unit.sp
import com.inidamleader.ovtracker.util.compose.SuggestedFontSizesStatus.Companion.rememberSuggestedFontSizesStatus
import com.inidamleader.ovtracker.util.compose.geometry.roundToPx
import com.inidamleader.ovtracker.util.compose.geometry.toIntSize
import com.inidamleader.ovtracker.util.compose.geometry.toSp
import kotlin.math.min

/**
 * Composable function that automatically adjusts the text size to fit within given constraints, considering the ratio of line spacing to text size.
 *
 * Features:
 *  1. Best performance: Utilizes a dichotomous binary search algorithm for swift and optimal text size determination without unnecessary iterations.
 *  2. Alignment support: Supports six possible alignment values via the Alignment interface.
 *  3. Material Design 3 support.
 *  4. Font scaling support: User-initiated font scaling doesn't affect the visual rendering output.
 *  5. Multiline Support with maxLines Parameter.
 *
 * @param text the text to be displayed
 * @param modifier the [Modifier] to be applied to this layout node
 * @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set,
 * this will be [LocalContentColor].
 * @param suggestedFontSizes The suggested font sizes to choose from (Should be sorted from smallest to largest, not ampty and contains only sp text unit).
 * @param suggestedFontSizesStatus Whether or not suggestedFontSizes is valid: not empty - contains oly sp text unit - sorted.
 * You can check validity by invoking [List<TextUnit>.suggestedFontSizesStatus]
 * @param stepGranularityTextSize The step size for adjusting the text size.
 * @param minTextSize The minimum text size allowed.
 * @param maxTextSize The maximum text size allowed.
 * @param fontStyle the typeface variant to use when drawing the letters (e.g., italic).
 * See [TextStyle.fontStyle].
 * @param fontWeight the typeface thickness to use when painting the text (e.g., [FontWeight.Bold]).
 * @param fontFamily the font family to be used when rendering the text. See [TextStyle.fontFamily].
 * @param letterSpacing the amount of space to add between each letter.
 * See [TextStyle.letterSpacing].
 * @param textDecoration the decorations to paint on the text (e.g., an underline).
 * See [TextStyle.textDecoration].
 * @param alignment The alignment of the text within its container.
 * @param overflow how visual overflow should be handled.
 * @param softWrap whether the text should break at soft line breaks. If false, the glyphs in the
 * text will be positioned as if there was unlimited horizontal space. If [softWrap] is false,
 * [overflow] and TextAlign may have unexpected effects.
 * @param maxLines An optional maximum number of lines for the text to span, wrapping if
 * necessary. If the text exceeds the given number of lines, it will be truncated according to
 * [overflow] and [softWrap]. It is required that 1 <= [minLines] <= [maxLines].
 * @param minLines The minimum height in terms of minimum number of visible lines. It is required
 * that 1 <= [minLines] <= [maxLines].
 * insert composables into text layout. See [InlineTextContent].
 * @param onTextLayout callback that is executed when a new text layout is calculated. A
 * [TextLayoutResult] object that callback provides contains paragraph information, size of the
 * text, baselines and other details. The callback can be used to add additional decoration or
 * functionality to the text. For example, to draw selection around the text.
 * @param style style configuration for the text such as color, font, line height etc.
 * @param lineSpacingRatio The ratio of line spacing to text size.
 *
 * @author Reda El Madini - For support, contact email@example.com
 */
@Composable
fun AutoSizeText(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    suggestedFontSizes: ImmutableWrapper<List<TextUnit>> = emptyList<TextUnit>().toImmutableWrapper(),
    suggestedFontSizesStatus: SuggestedFontSizesStatus = suggestedFontSizes.rememberSuggestedFontSizesStatus,
    stepGranularityTextSize: TextUnit = TextUnit.Unspecified,
    minTextSize: TextUnit = TextUnit.Unspecified,
    maxTextSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    alignment: Alignment = Alignment.TopStart,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    minLines: Int = 1,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current,
    lineSpacingRatio: Float = style.lineHeight.value / style.fontSize.value,
) {
    AutoSizeText(
        text = AnnotatedString(text),
        modifier = modifier,
        color = color,
        suggestedFontSizes = suggestedFontSizes,
        suggestedFontSizesStatus = suggestedFontSizesStatus,
        stepGranularityTextSize = stepGranularityTextSize,
        minTextSize = minTextSize,
        maxTextSize = maxTextSize,
        fontStyle = fontStyle,
        fontWeight = fontWeight,
        fontFamily = fontFamily,
        letterSpacing = letterSpacing,
        textDecoration = textDecoration,
        alignment = alignment,
        overflow = overflow,
        softWrap = softWrap,
        maxLines = maxLines,
        minLines = minLines,
        onTextLayout = onTextLayout,
        style = style,
        lineSpacingRatio = lineSpacingRatio,
    )
}

/**
 * Composable function that automatically adjusts the text size to fit within given constraints using AnnotatedString, considering the ratio of line spacing to text size.
 *
 * Features:
 *  Similar to AutoSizeText(String), with support for AnnotatedString.
 *
 * @param inlineContent a map storing composables that replaces certain ranges of the text, used to
 * insert composables into text layout. See [InlineTextContent].
 * @see AutoSizeText
 */
@Composable
fun AutoSizeText(
    text: AnnotatedString,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    suggestedFontSizes: ImmutableWrapper<List<TextUnit>> = emptyList<TextUnit>().toImmutableWrapper(),
    suggestedFontSizesStatus: SuggestedFontSizesStatus = suggestedFontSizes.rememberSuggestedFontSizesStatus,
    stepGranularityTextSize: TextUnit = TextUnit.Unspecified,
    minTextSize: TextUnit = TextUnit.Unspecified,
    maxTextSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    alignment: Alignment = Alignment.TopStart,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    minLines: Int = 1,
    inlineContent: ImmutableWrapper<Map<String, InlineTextContent>> = mapOf<String, InlineTextContent>().toImmutableWrapper(),
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current,
    lineSpacingRatio: Float = style.lineHeight.value / style.fontSize.value,
) {
    val density = LocalDensity.current
    // Change font scale to 1F
    CompositionLocalProvider(
        LocalDensity provides Density(density = density.density, fontScale = 1F)
    ) {
        BoxWithConstraints(
            modifier = modifier,
            contentAlignment = alignment,
        ) {
            val combinedTextStyle = LocalTextStyle.current + style.copy(
                color = color.takeIf { it.isSpecified } ?: style.color,
                fontStyle = fontStyle ?: style.fontStyle,
                fontWeight = fontWeight ?: style.fontWeight,
                fontFamily = fontFamily ?: style.fontFamily,
                letterSpacing = letterSpacing.takeIf { it.isSpecified } ?: style.letterSpacing,
                textDecoration = textDecoration ?: style.textDecoration,
                textAlign = when (alignment) {
                    Alignment.TopStart, Alignment.CenterStart, Alignment.BottomStart -> TextAlign.Start
                    Alignment.TopCenter, Alignment.Center, Alignment.BottomCenter -> TextAlign.Center
                    Alignment.TopEnd, Alignment.CenterEnd, Alignment.BottomEnd -> TextAlign.End
                    else -> TextAlign.Unspecified
                },
            )

            val layoutDirection = LocalLayoutDirection.current
            val currentDensity = LocalDensity.current
            val fontFamilyResolver = LocalFontFamilyResolver.current
            val coercedLineSpacingRatio = lineSpacingRatio.takeIf { it.isFinite() && it >= 1 } ?: 1F
            val shouldMoveBackward: (TextUnit) -> Boolean = {
                shouldShrink(
                    text = text,
                    textStyle = combinedTextStyle.copy(
                        fontSize = it,
                        lineHeight = it * coercedLineSpacingRatio,
                    ),
                    maxLines = maxLines,
                    minLines = minLines,
                    softWrap = softWrap,
                    layoutDirection = layoutDirection,
                    density = currentDensity,
                    fontFamilyResolver = fontFamilyResolver,
                )
            }

            val electedFontSize = kotlin.run {
                if (suggestedFontSizesStatus == SuggestedFontSizesStatus.VALID)
                    suggestedFontSizes.value
                else
                    remember(suggestedFontSizes) {
                        suggestedFontSizes.value
                            .filter { it.isSp }
                            .takeIf { it.isNotEmpty() }
                            ?.sortedBy { it.value }
                    }
            }
                ?.findElectedValue(shouldMoveBackward = shouldMoveBackward)
                ?: rememberCandidateFontSizesIntProgress(
                    density = density,
                    dpSize = DpSize(maxWidth, maxHeight),
                    maxTextSize = maxTextSize,
                    minTextSize = minTextSize,
                    stepGranularityTextSize = stepGranularityTextSize,
                ).findElectedValue(
                    transform = { density.toSp(it) },
                    shouldMoveBackward = shouldMoveBackward,
                )

            Text(
                text = text,
                overflow = overflow,
                softWrap = softWrap,
                maxLines = maxLines,
                minLines = minLines,
                inlineContent = inlineContent.value,
                onTextLayout = onTextLayout,
                style = combinedTextStyle.copy(
                    fontSize = electedFontSize,
                    lineHeight = electedFontSize * coercedLineSpacingRatio,
                ),
            )
        }
    }
}

@OptIn(InternalFoundationTextApi::class)
private fun BoxWithConstraintsScope.shouldShrink(
    text: AnnotatedString,
    textStyle: TextStyle,
    maxLines: Int,
    minLines: Int,
    softWrap: Boolean,
    layoutDirection: LayoutDirection,
    density: Density,
    fontFamilyResolver: FontFamily.Resolver,
) = TextDelegate(
    text = text,
    style = textStyle,
    maxLines = maxLines,
    minLines = minLines,
    softWrap = softWrap,
    overflow = TextOverflow.Clip,
    density = density,
    fontFamilyResolver = fontFamilyResolver,
).layout(
    constraints = constraints,
    layoutDirection = layoutDirection,
).hasVisualOverflow

@Composable
private fun rememberCandidateFontSizesIntProgress(
    density: Density,
    dpSize: DpSize,
    minTextSize: TextUnit = TextUnit.Unspecified,
    maxTextSize: TextUnit = TextUnit.Unspecified,
    stepGranularityTextSize: TextUnit = TextUnit.Unspecified,
): IntProgression {
    val max = remember(maxTextSize, dpSize, density) {
        val intSize = density.toIntSize(dpSize)
        min(intSize.width, intSize.height).let { max ->
            maxTextSize
                .takeIf { it.isSp }
                ?.let { density.roundToPx(it) }
                ?.coerceIn(range = 0..max)
                ?: max
        }
    }

    val min = remember(minTextSize, max, density) {
        minTextSize
            .takeIf { it.isSp }
            ?.let { density.roundToPx(it) }
            ?.coerceIn(range = 0..max)
            ?: 0
    }

    val step = remember(stepGranularityTextSize, min, max, density) {
        stepGranularityTextSize
            .takeIf { it.isSp }
            ?.let { density.roundToPx(it) }
            ?.coerceAtLeast(minimumValue = 1)
            ?: 1
    }

    return remember(min, max, step) {
        min..max step step
    }
}

// This function works by using a binary search algorithm
fun <E> List<E>.findElectedValue(shouldMoveBackward: (E) -> Boolean) = run {
    indices.findElectedValue(
        transform = { this[it] },
        shouldMoveBackward = shouldMoveBackward,
    )
}

// This function works by using a binary search algorithm
private fun <E> IntProgression.findElectedValue(
    transform: (Int) -> E,
    shouldMoveBackward: (E) -> Boolean,
) = run {
    var low = first
    var high = last
    while (low <= high) {
        val mid = low + (high - low) / 2
        if (shouldMoveBackward(transform(mid)))
            high = mid - 1
        else
            low = mid + 1
    }
    transform(high.coerceAtLeast(minimumValue = first))
}

enum class SuggestedFontSizesStatus {
    VALID, INVALID, UNKNOWN;

    companion object {
        val List<TextUnit>.suggestedFontSizesStatus
            get() = if (isNotEmpty() && all { it.isSp } && sortedBy { it.value } == this)
                VALID
            else
                INVALID
        val ImmutableWrapper<List<TextUnit>>.rememberSuggestedFontSizesStatus
            @Composable get() = remember(this) { value.suggestedFontSizesStatus }
    }
}

@Preview(widthDp = 200, heightDp = 100)
@Preview(widthDp = 200, heightDp = 30)
@Preview(widthDp = 60, heightDp = 30)
@Composable
fun PreviewAutoSizeTextWithMaxLinesSetToIntMaxValue() {
    MaterialTheme {
        Surface(color = MaterialTheme.colorScheme.primary) {
            AutoSizeText(
                text = "This is a bunch of text that will be auto sized",
                modifier = Modifier.fillMaxSize(),
                alignment = Alignment.CenterStart,
                style = MaterialTheme.typography.bodyMedium,
            )
        }
    }
}

@Preview(widthDp = 200, heightDp = 100)
@Preview(widthDp = 200, heightDp = 30)
@Preview(widthDp = 60, heightDp = 30)
@Composable
fun PreviewAutoSizeTextWithMinSizeSetTo14() {
    MaterialTheme {
        Surface(color = MaterialTheme.colorScheme.secondary) {
            AutoSizeText(
                text = "This is a bunch of text that will be auto sized",
                modifier = Modifier.fillMaxSize(),
                minTextSize = 14.sp,
                alignment = Alignment.CenterStart,
                overflow = TextOverflow.Ellipsis,
                style = MaterialTheme.typography.bodyMedium,
            )
        }
    }
}

@Preview(widthDp = 200, heightDp = 100)
@Preview(widthDp = 200, heightDp = 30)
@Preview(widthDp = 60, heightDp = 30)
@Composable
fun PreviewAutoSizeTextWithMaxLinesSetToOne() {
    MaterialTheme {
        Surface(color = MaterialTheme.colorScheme.tertiary) {
            AutoSizeText(
                text = "This is a bunch of text that will be auto sized",
                modifier = Modifier.fillMaxSize(),
                alignment = Alignment.Center,
                maxLines = 1,
                style = MaterialTheme.typography.bodyMedium
            )
        }
    }
}

DensityExt.kt:
package com.inidamleader.ovtracker.util.compose.geometry

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.isSpecified

// DP
fun Density.toSp(dp: Dp): TextUnit = dp.toSp()
fun Density.toPx(dp: Dp): Float = dp.toPx()
fun Density.roundToPx(dp: Dp): Int = dp.roundToPx()

// TEXT UNIT
fun Density.toDp(sp: TextUnit): Dp = sp.toDp()
fun Density.toPx(sp: TextUnit): Float = sp.toPx()
fun Density.roundToPx(sp: TextUnit): Int = sp.roundToPx()

// FLOAT
fun Density.toDp(px: Float): Dp = px.toDp()
fun Density.toSp(px: Float): TextUnit = px.toSp()

// INT
fun Density.toDp(px: Int): Dp = px.toDp()
fun Density.toSp(px: Int): TextUnit = px.toSp()

// SIZE
fun Density.toIntSize(dpSize: DpSize): IntSize =
    IntSize(dpSize.width.roundToPx(), dpSize.height.roundToPx())

fun Density.toSize(dpSize: DpSize): Size =
    if (dpSize.isSpecified) Size(dpSize.width.toPx(), dpSize.height.toPx())
    else Size.Unspecified

fun Density.toDpSize(size: Size): DpSize =
    if (size.isSpecified) DpSize(size.width.toDp(), size.height.toDp())
    else DpSize.Unspecified

fun Density.toDpSize(intSize: IntSize): DpSize =
    DpSize(intSize.width.toDp(), intSize.height.toDp())

// OFFSET
fun Density.toIntOffset(dpOffset: DpOffset): IntOffset =
    IntOffset(dpOffset.x.roundToPx(), dpOffset.y.roundToPx())

fun Density.toOffset(dpOffset: DpOffset): Offset =
    if (dpOffset.isSpecified) Offset(dpOffset.x.toPx(), dpOffset.y.toPx())
    else Offset.Unspecified

fun Density.toDpOffset(offset: Offset): DpOffset =
    if (offset.isSpecified) DpOffset(offset.x.toDp(), offset.y.toDp())
    else DpOffset.Unspecified

fun Density.toDpOffset(intOffset: IntOffset): DpOffset =
    DpOffset(intOffset.x.toDp(), intOffset.y.toDp())

ImmutableWrapper.kt:
package com.inidamleader.ovtracker.util.compose

import androidx.compose.runtime.Immutable
import kotlin.reflect.KProperty

@Immutable
data class ImmutableWrapper<T>(val value: T)

fun <T> T.toImmutableWrapper() = ImmutableWrapper(this)

operator fun <T> ImmutableWrapper<T>.getValue(thisRef: Any?, property: KProperty<*>) = value

AutoSizeTextTest.kt:
package com.inidamleader.ovtracker.util.compose

import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
import com.inidamleader.ovtracker.util.compose.SuggestedFontSizesStatus.Companion.suggestedFontSizesStatus
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
import kotlin.test.assertTrue

class AutoSizeTextTest {
    @Test
    fun findElectedIndexThrowException() {
        List(size = 0) { it }.run {
            assertFails { findElectedValue { false } }
        }
        List(size = 0) { it }.run {
            assertFails { findElectedValue { true } }
        }
    }

    @Test
    fun findElectedValueAt0() {
        List(size = 1) { it }.run {
            assertEquals(
                expected = 0,
                actual = findElectedValue { false },
            )
        }

        List(size = 1) { it }.run {
            assertEquals(
                expected = 0,
                actual = findElectedValue { true },
            )
        }

        List(size = 2) { it }.run {
            assertEquals(
                expected = 0,
                actual = findElectedValue { true },
            )
        }

        List(size = 15) { it }.run {
            assertEquals(
                expected = 0,
                actual = findElectedValue { true },
            )
        }

        List(size = 16) { it }.run {
            assertEquals(
                expected = 0,
                actual = findElectedValue { true },
            )
        }
    }

    @Test
    fun findElectedValueAt1() {
        List(size = 2) { it }.run {
            assertEquals(
                expected = 1,
                actual = findElectedValue { false },
            )
        }

        List(size = 2) { it }.run {
            assertEquals(
                expected = 1,
                actual = findElectedValue { it > 1 },
            )
        }

        List(size = 15) { it }.run {
            assertEquals(
                expected = 1,
                actual = findElectedValue { it > 1 },
            )
        }

        List(size = 16) { it }.run {
            assertEquals(
                expected = 1,
                actual = findElectedValue { it > 1 },
            )
        }
    }

    @Test
    fun findElectedValueAt2() {
        List(size = 3) { it }.run {
            assertEquals(
                expected = 2,
                actual = findElectedValue { false },
            )
        }

        List(size = 14) { it }.run {
            assertEquals(
                expected = 2,
                actual = findElectedValue { it > 2 },
            )
        }

        List(size = 15) { it }.run {
            assertEquals(
                expected = 2,
                actual = findElectedValue { it > 2 },
            )
        }

        List(size = 16) { it }.run {
            assertEquals(
                expected = 2,
                actual = findElectedValue { it > 2 },
            )
        }
    }

    @Test
    fun findElectedValueAt9() {
        List(size = 10) { it }.run {
            assertEquals(
                expected = 9,
                actual = findElectedValue { false },
            )
        }

        List(size = 14) { it }.run {
            assertEquals(
                expected = 9,
                actual = findElectedValue { it > 9 },
            )
        }

        List(size = 15) { it }.run {
            assertEquals(
                expected = 9,
                actual = findElectedValue { it > 9 },
            )
        }

        List(size = 16) { it }.run {
            assertEquals(
                expected = 9,
                actual = findElectedValue { it > 9 },
            )
        }
    }

    @Test
    fun findElectedValueAt10() {
        List(size = 11) { it }.run {
            assertEquals(
                expected = 10,
                actual = findElectedValue { false },
            )
        }

        List(size = 14) { it }.run {
            assertEquals(
                expected = 10,
                actual = findElectedValue { it > 10 },
            )
        }

        List(size = 15) { it }.run {
            assertEquals(
                expected = 10,
                actual = findElectedValue { it > 10 },
            )
        }

        List(size = 16) { it }.run {
            assertEquals(
                expected = 10,
                actual = findElectedValue { it > 10 },
            )
        }
    }

    @Test
    fun suggestedFontSizesStatus() {
        assertTrue {
            listOf(1.sp, 2.sp, 4.sp).suggestedFontSizesStatus == SuggestedFontSizesStatus.VALID
        }

        assertTrue {
            listOf(2.sp, 1.sp, 4.sp).suggestedFontSizesStatus == SuggestedFontSizesStatus.INVALID
        }

        assertTrue {
            emptyList<TextUnit>().suggestedFontSizesStatus == SuggestedFontSizesStatus.INVALID
        }

        assertTrue {
            listOf(1.sp, 2.em, 4.sp).suggestedFontSizesStatus == SuggestedFontSizesStatus.INVALID
        }
    }
}

Comment: Track update on https://gist.github.com/inidamleader/b594d35362ebcf3cedf81055df519300
Answer: (Works with preview) Here's another solution using BoxWithConstraints to get the available width and compare it to the width that's needed to lay out the Text in one line, using ParagraphIntrinsics:
@Composable
private fun AutosizeText(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
) {
    BoxWithConstraints {
        var shrunkFontSize = fontSize
        val calculateIntrinsics = @Composable {
            ParagraphIntrinsics(
                text, TextStyle(
                    color = color,
                    fontSize = shrunkFontSize,
                    fontWeight = fontWeight,
                    textAlign = textAlign,
                    lineHeight = lineHeight,
                    fontFamily = fontFamily,
                    textDecoration = textDecoration,
                    fontStyle = fontStyle,
                    letterSpacing = letterSpacing
                ),
                density = LocalDensity.current,
                resourceLoader = LocalFontLoader.current
            )
        }

        var intrinsics = calculateIntrinsics()
        with(LocalDensity.current) {
            while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
                shrunkFontSize *= 0.9
                intrinsics = calculateIntrinsics()
            }
        }
        Text(
            text = text,
            modifier = modifier,
            color = color,
            fontSize = shrunkFontSize,
            fontStyle = fontStyle,
            fontWeight = fontWeight,
            fontFamily = fontFamily,
            letterSpacing = letterSpacing,
            textDecoration = textDecoration,
            textAlign = textAlign,
            lineHeight = lineHeight,
            onTextLayout = onTextLayout,
            style = style
        )
    }
}

Comment: Thanks! This is very cool, do you know how we can achieve this for multi lines as well? For instance if I want the text to be 5 lines.
Also, maybe the fontSize should not have a default value as TextUnit.Unspecifified as it won't work without setting an initial font size, if I'm not mistaken.
Comment: Yes you're right, for multi lines I think `while (intrinsics.maxIntrinsicWidth > maxWidth.toPx() * lineNumber)` should work, but I haven't played around with that yet
Comment: Thanks for this solution! I posted a modified version that takes height and maxLines into account.
Answer: I'd like to add that, if you do not want to see the middle states from the answer of @Brian, you can try this.
        modifier = Modifier
            .drawWithContent {
                if (calculationFinish) { // replace by your logic
                    drawContent()
                }
            },

Answer: I found that in @EmbMicro answer maxlines sometimes gets ignored. I fixed that issue and also replaced the deprecated call to Paragraph with constraints instead of with
@Composable
fun AutoSizeText(
    text: String,
    modifier: Modifier = Modifier,
    acceptableError: Dp = 5.dp,
    maxFontSize: TextUnit = TextUnit.Unspecified,
    color: Color = Color.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    maxLines: Int = Int.MAX_VALUE,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
) {
    BoxWithConstraints(modifier = modifier) {
        var shrunkFontSize = if (maxFontSize.isSpecified) maxFontSize else 100.sp

        val calculateIntrinsics = @Composable {
            val mergedStyle = style.merge(
                TextStyle(
                    color = color,
                    fontSize = shrunkFontSize,
                    fontWeight = fontWeight,
                    textAlign = textAlign,
                    lineHeight = lineHeight,
                    fontFamily = fontFamily,
                    textDecoration = textDecoration,
                    fontStyle = fontStyle,
                    letterSpacing = letterSpacing
                )
            )
            Paragraph(
                text = text,
                style = mergedStyle,
                spanStyles = listOf(),
                placeholders = listOf(),
                maxLines = maxLines,
                ellipsis = false,
                constraints = Constraints(maxWidth = ceil(LocalDensity.current.run { maxWidth.toPx() }).toInt()) ,
                density = LocalDensity.current,
                fontFamilyResolver = LocalFontFamilyResolver.current
            )
        }

        var intrinsics = calculateIntrinsics()

        val targetWidth = maxWidth - acceptableError / 2f

        with(LocalDensity.current) {
            if (maxFontSize.isUnspecified || targetWidth < intrinsics.minIntrinsicWidth.toDp() || intrinsics.didExceedMaxLines) {
                while ((targetWidth - intrinsics.minIntrinsicWidth.toDp()).toPx().absoluteValue.toDp() > acceptableError / 2f) {
                    shrunkFontSize *= targetWidth.toPx() / intrinsics.minIntrinsicWidth
                    intrinsics = calculateIntrinsics()
                }
                while (intrinsics.didExceedMaxLines || maxHeight < intrinsics.height.toDp()) {
                    shrunkFontSize *= 0.9f
                    intrinsics = calculateIntrinsics()
                }
            }
        }

        if (maxFontSize.isSpecified && shrunkFontSize > maxFontSize)
            shrunkFontSize = maxFontSize

        Text(
            text = text,
            color = color,
            fontSize = shrunkFontSize,
            fontStyle = fontStyle,
            fontWeight = fontWeight,
            fontFamily = fontFamily,
            letterSpacing = letterSpacing,
            textDecoration = textDecoration,
            textAlign = textAlign,
            lineHeight = lineHeight,
            onTextLayout = onTextLayout,
            maxLines = maxLines,
            style = style
        )
    }
}

Comment: left a comment on @EmbMicro answer that I think would also apply here :)
Answer: I solved my problem with the following code:
@Composable
fun AutoSizeText(
    modifier: Modifier,
    text: String,
    textStyle: TextStyle,
    maxLines: Int = 1,
) {
    val textMeasurer = rememberTextMeasurer()
    var textLayoutResult: TextLayoutResult? = remember { null }
    Box(modifier = modifier
        .layout { measurable, constraints ->
            val placeable = measurable.measure(constraints)
            var fontSize = textStyle.fontSize
            do {
                textLayoutResult = textMeasurer.measure(
                    text = text,
                    style = textStyle.copy(fontSize = fontSize),
                    constraints = constraints,
                    maxLines = maxLines
                )
                // fontSize = fontSize.unaryMinus()
                fontSize *= 0.95f
            } while (textLayoutResult!!.didOverflowHeight)
            val box = textLayoutResult!!.size
            layout(box.width, box.height) {
                placeable.place(0, 0)
            }
        }
        .drawWithContent {
            textLayoutResult?.let { drawText(it) }
        })
}

Answer: Update: This may have stopped working after the 1.0.1 release....
Another way to do this inspired by @nieto's answer is to resize without recomposing by just manually measuring using the paragraph block given the inbound constraints.  Also previews correctly as a bonus

@Composable
fun AutoSizeText(
    text: String,
    style: TextStyle,
    modifier: Modifier = Modifier,
    minTextSize: TextUnit = TextUnit.Unspecified,
    maxLines: Int = Int.MAX_VALUE,
) {
    BoxWithConstraints(modifier) {
        var combinedTextStyle = LocalTextStyle.current + style

        while (shouldShrink(text, combinedTextStyle, minTextSize, maxLines)) {
            combinedTextStyle = combinedTextStyle.copy(fontSize = combinedTextStyle.fontSize * .9f)
        }

        Text(
            text = text,
            style = style + TextStyle(fontSize = combinedTextStyle.fontSize),
            maxLines = maxLines,
        )
    }
}

@Composable
private fun BoxWithConstraintsScope.shouldShrink(
    text: String,
    combinedTextStyle: TextStyle,
    minimumTextSize: TextUnit,
    maxLines: Int
): Boolean = if (minimumTextSize == TextUnit.Unspecified || combinedTextStyle.fontSize > minimumTextSize) {
    false
} else {
    val paragraph = Paragraph(
        text = text,
        style = combinedTextStyle,
        width = maxWidth.value,
        maxLines = maxLines,
        density = LocalDensity.current,
        resourceLoader = LocalFontLoader.current,
    )
    paragraph.height > maxHeight.value
}

Answer: Tweaked a little solution from Thad C
Compose version: 1.1.0-beta02
Preview works
No blinking when text changes, text changes are handled quickly (though would be even better if text size calculation would be launched as coroutine on another thread)
@Composable
fun AutoSizeText(
    text: AnnotatedString,
    minTextSizeSp: Float,
    maxTextSizeSp: Float,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    textAlign: TextAlign? = null,
    style: TextStyle = LocalTextStyle.current,
    contentAlignment: Alignment = Alignment.TopStart,
) {
    check(minTextSizeSp > 0) { "Min text size should above zero" }
    check(minTextSizeSp < maxTextSizeSp) { "Min text size should be smaller then max text size" }
    BoxWithConstraints(modifier, contentAlignment = contentAlignment) {
        val textString = text.toString()
        val currentStyle = style.copy(
            color = color,
            fontStyle = fontStyle ?: style.fontStyle,
            fontSize = maxTextSizeSp.sp,
            fontWeight = fontWeight ?: style.fontWeight,
            fontFamily = fontFamily ?: style.fontFamily,
            textAlign = textAlign,
        )
        val fontChecker = createFontChecker(currentStyle, textString)
        val fontSize = remember(textString) {
            fontChecker.findMaxFittingTextSize(minTextSizeSp, maxTextSizeSp)
        }

        Text(
            text = text,
            style = currentStyle + TextStyle(fontSize = fontSize),
            color = color,
            textAlign = textAlign
        )
    }
}

@Composable
private fun BoxWithConstraintsScope.createFontChecker(currentStyle: TextStyle, text: String): FontChecker {
    val density = LocalDensity.current
    return FontChecker(
        density = density,
        resourceLoader = LocalFontLoader.current,
        maxWidthPx = with (density) { maxWidth.toPx() },
        maxHeightPx = with (density) { maxHeight.toPx() },
        currentStyle = currentStyle,
        text = text
    )
}

private class FontChecker(
    private val density: Density,
    private val resourceLoader: Font.ResourceLoader,
    private val maxWidthPx: Float,
    private val maxHeightPx: Float,
    private val currentStyle: TextStyle,
    private val text: String
) {

    fun isFit(fontSizeSp: Float): Boolean {
        val height = Paragraph(
            text = text,
            style = currentStyle + TextStyle(fontSize = fontSizeSp.sp),
            width = maxWidthPx,
            density = density,
            resourceLoader = resourceLoader,
        ).height
        return height <= maxHeightPx
    }

    fun findMaxFittingTextSize(
        minTextSizeSp: Float,
        maxTextSizeSp: Float
    ) = if (!isFit(minTextSizeSp)) {
        minTextSizeSp.sp
    } else if (isFit(maxTextSizeSp)) {
        maxTextSizeSp.sp
    } else {
        var fit = minTextSizeSp
        var unfit = maxTextSizeSp
        while (unfit - fit > 1) {
            val current = fit + (unfit - fit) / 2
            if (isFit(current)) {
                fit = current
            } else {
                unfit = current
            }
        }
        fit.sp
    }
}

Answer: I adapted @biodun's solution, which is based on onTextLayout and hasVisualOverflow. I have added a minimum font size, and created a composable:
/**
 * This is a composable that mostly has the same API (parameters & defaults) as Text.kt,
 * just with the difference that the font size is changed to accommodate the entire text if possible.
 *
 * Note: Unfortunately this does NOT work in a preview. Previews seem to only layout the text once, and thus,
 * the fontMultiplier only gets changed to 0.95.
 */
@Composable
fun AutoSizeText(
    text: String,
    minFontSize: TextUnit,
    desiredFontSize: TextUnit,
    maxLines: Int,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    minLines: Int = 1,
    style: TextStyle = LocalTextStyle.current
) {
    // Adapted from https://stackoverflow.com/a/68449520/3991578, added minFontSize
    var fontMultiplier by remember { mutableFloatStateOf(1f) }
    fun updateFontSize(multiplier: Float) {
        val result = desiredFontSize * multiplier
        // Make sure the font size doesn't get too small
        if (result > minFontSize) {
            fontMultiplier = multiplier
        }
    }
    Text(
        text = text,
        modifier = modifier,
        color = color,
        fontStyle = fontStyle,
        fontWeight = fontWeight,
        fontFamily = fontFamily,
        letterSpacing = letterSpacing,
        textDecoration = textDecoration,
        textAlign = textAlign,
        lineHeight = lineHeight,
        overflow = overflow,
        softWrap = softWrap,
        maxLines = maxLines,
        minLines = minLines,
        onTextLayout = {
            if (it.hasVisualOverflow) {
                updateFontSize(fontMultiplier * 0.95f)
            }
        },
        style = style.copy(
            fontSize = desiredFontSize * fontMultiplier
        )
    )
}

Answer: This is based on Mohammad's answer.
You have to find a better way to calculate the font size by using the box's height and message's length.
@Composable
fun Greeting() {
    var width by remember { mutableStateOf(0) }
    var height by remember { mutableStateOf(0) }
    val msg = "My really long long long long long text that needs to be resized to the height of this Column"
    Column(modifier = Modifier.height(150.dp).padding(8.dp).background(Color.Blue).onPositioned {
        width = it.size.width
        height = it.size.height
    }) {
        Log.d("mainactivity", "width = $width")
        Log.d("mainactivity", "height = $height")
        Text(
                modifier = Modifier.background(Color.Green).fillMaxHeight(),
                style = TextStyle(fontSize = calculateFontSize(msg, height).sp),
                text = msg
        )
    }
}

fun calculateFontSize(msg: String, height: Int): Int {
    return height / (msg.length / 5)
}

Answer: I had a need to implement autosizeable text with list of my own fonts.
So, here is my implementation based on Lit Climbing's answer
Answer: This option works in real time.
The solution is based on the size of the container in which the text is placed. This works for both height and width.
Important details:

result.layoutInput.constraints.maxHeight
result.layoutInput.constraints.maxWidth
val localDensity = LocalDensity.current
fontSize = with(localDensity) { *.toSp() }

Solution:
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit

@Composable
fun AutoSizeText(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    maxLines: Int = Int.MAX_VALUE,
    style: TextStyle = LocalTextStyle.current
) {
    var scaledTextStyle by remember { mutableStateOf(style) }

    val localDensity = LocalDensity.current

    Text(
        text = text,
        color = color,
        maxLines = maxLines,
        fontStyle = fontStyle,
        fontWeight = fontWeight,
        fontFamily = fontFamily,
        letterSpacing = letterSpacing,
        textDecoration = textDecoration,
        textAlign = textAlign,
        lineHeight = lineHeight,
        overflow = overflow,
        softWrap = false,
        style = scaledTextStyle,
        onTextLayout = { result ->
            val scaleByHeight =
                result.layoutInput.constraints.maxHeight / 2
            val scaleByWidth =
                result.layoutInput.constraints.maxWidth / result.layoutInput.text.length
            scaledTextStyle = scaledTextStyle.copy(
                fontSize = with(localDensity) {
                    if (scaleByHeight < scaleByWidth)
                        scaleByHeight.toSp()
                    else
                        scaleByWidth.toSp()
                }
            )
        },
        modifier = modifier.drawWithContent { drawContent() }
    )
}

Answer: try BoxWithConstraints, and learn the SubcomposeLayout concept
BoxWithConstraints(
    modifier = Modifier
        .fillMaxWidth()
        .weight(5f)
) {
    val size = min(maxWidth * 1.7f, maxHeight)
    val fontSize = size * 0.8f
    Text(
        text = first,
        color = color,
        fontSize = LocalDensity.current.run { fontSize.toSp() },
        modifier = Modifier.fillMaxSize(),
        textAlign = TextAlign.Center,
    )
}

Comment: This calculates the text composable with respect to the (outer) layout. But it does not take  the text itself into account. So, the text would render always with the same font size, no matter whether you have 10 characters or 100.
Comment: @Brian Yep, it's quite simple. To implement an AutoResizeTextView-ish would be more complicated.


Add chronological number to a column if empty

Question: I have a dataframe with a column called 's_nummer'. This column is sometimes NA and in that case, I would like to add a number myself that can range from 700001 to 800000. So in this case, row numbers 3 and 4 do not contain a value in the s_nummer column and I would like to add the values 700001 to row 3 and 700002 to row 4.
dput:
structure(list(s_nummer = c(599999, 599999, NA, NA), eerste_voornaam = c("Debbie",
"Debbie", "Debbie", "Debbie"), tussenvoegsel = c(NA, NA, NA,
NA), geslachtsnaam = c("Oomen", "Oomen", "Oomen", "Oomen")), row.names = c(NA,
-4L), class = c("tbl_df", "tbl", "data.frame"))

Hope you can help!
Thanks in advance
Answer: You can use which with is.na to get the lines with NA in x$s_nummer and overwrite them with 700000 + seq_along.
i <- which(is.na(x$s_nummer))
x$s_nummer[i] <- 700000 + seq_along(i)
#  s_nummer eerste_voornaam tussenvoegsel geslachtsnaam
#1   599999          Debbie            NA         Oomen
#2   599999          Debbie            NA         Oomen
#3   700001          Debbie            NA         Oomen
#4   700002          Debbie            NA         Oomen