diff --git a/App/PhotoDemon/Languages/French.xml b/App/PhotoDemon/Languages/French.xml
index 150b592f06..71cfec087b 100644
--- a/App/PhotoDemon/Languages/French.xml
+++ b/App/PhotoDemon/Languages/French.xml
@@ -6,7 +6,7 @@
fr-FR
Français
-6.7.592
+6.7.593
Complete
Jean Jacques Piedfort (orig. Frank Donckers)
@@ -3030,6 +3030,11 @@ Description: %2
reflection
+
+last line justify
+
+
+
Modify selection
Modification de la sélection
@@ -3052,7 +3057,7 @@ When finished, click the Submit New Issue button. Thank you!
Instructions pour les rapports de bogues
-
+
automatic
@@ -12705,7 +12710,7 @@ Vous pouvez alors si besoin
Cliquer ici pour convertir en calque de texte avancé
-
+
click here for detailed instructions (in English)
@@ -13966,10 +13971,10 @@ Si vous choisissez de désactiver les mises à jour, n'oubliez pas de visiter ph
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/German.xml b/App/PhotoDemon/Languages/German.xml
index 429aa6f1e3..cd45ffb84b 100644
--- a/App/PhotoDemon/Languages/German.xml
+++ b/App/PhotoDemon/Languages/German.xml
@@ -6,7 +6,7 @@
de-DE
Deutsch (German)
-9.0.1705
+9.0.1706
Up-to-date
rk (ehem. Frank Donckers, Helmut Kuerbiss)
@@ -3030,6 +3030,11 @@ Deskription: %2
Spiegelung
+
+last line justify
+
+
+
Modify selection
Auswahl modifizieren
@@ -3054,7 +3059,7 @@ Nach Abschluss klicken Sie auf den Button "Neues Problem einreichen". Danke!Bug-Bericht-Instruktionen
-
+
automatic
@@ -12699,7 +12704,7 @@ Möchten Sie Ihre Batchliste vor dem Beenden speichern?
Klicken Sie hier, um diesen Layer in erweiterten Text zu konvertieren.
-
+
click here for detailed instructions (in English)
@@ -13955,10 +13960,10 @@ Wenn Sie sich dennoch dafür entscheiden, Updates zu deaktivieren, vergessen Sie
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Indonesian.xml b/App/PhotoDemon/Languages/Indonesian.xml
index ca2254ac00..519426420b 100644
--- a/App/PhotoDemon/Languages/Indonesian.xml
+++ b/App/PhotoDemon/Languages/Indonesian.xml
@@ -6,7 +6,7 @@
indo-ID
Indonesian
-8.9.1699
+8.9.1700
90% complete
Ari Sohandri Putra
@@ -3021,6 +3021,11 @@ Deskripsi: %2
+
+last line justify
+
+
+
Modify selection
@@ -3043,7 +3048,7 @@ When finished, click the Submit New Issue button. Thank you!
Intruksi Laporan Bug
-
+
automatic
@@ -12685,7 +12690,7 @@ Would kemudian anda suka untuk menyimpan senarai kumpulan anda sebelum keluar?
-
+
click here for detailed instructions (in English)
@@ -13937,10 +13942,10 @@ Jika anda masih memilih untuk melumpuhkan Perbarui, jangan lupa untuk mengunjung
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Italian.xml b/App/PhotoDemon/Languages/Italian.xml
index 449be699fd..f591dc2525 100644
--- a/App/PhotoDemon/Languages/Italian.xml
+++ b/App/PhotoDemon/Languages/Italian.xml
@@ -6,7 +6,7 @@
it-IT
Italiano
-8.9.1607
+8.9.1608
Completa
GioRock, ManfroMarce
@@ -3029,6 +3029,11 @@ Descrizione: %2
mirroring
+
+last line justify
+
+
+
Modify selection
Modificare la selezione
@@ -3053,7 +3058,7 @@ When finished, click the Submit New Issue button. Thank you!
Istruzioni per la segnalazione di bug
-
+
automatic
@@ -12708,7 +12713,7 @@ Vuoi salvare la tua lista prima di uscire?
Clicca qui per convertire questo livello in testo avanzato.
-
+
click here for detailed instructions (in English)
@@ -13964,10 +13969,10 @@ Se si sceglie comunque di disabilitare gli aggiornamenti, non dimenticate di vis
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Macedonian.xml b/App/PhotoDemon/Languages/Macedonian.xml
index 7e23f0d29f..a2b601d775 100644
--- a/App/PhotoDemon/Languages/Macedonian.xml
+++ b/App/PhotoDemon/Languages/Macedonian.xml
@@ -6,7 +6,7 @@
mk-MK
Македонски
-8.9.1701
+8.9.1702
80% complete
Бобан Ѓерасимоски
@@ -3025,6 +3025,11 @@ Error број %
+
+last line justify
+
+
+
Modify selection
@@ -3047,7 +3052,7 @@ When finished, click the Submit New Issue button. Thank you!
инструкциите за пријавување на бубачки
-
+
automatic
@@ -12691,7 +12696,7 @@ Would сакате да ги зачувате вашата листа сериј
-
+
click here for detailed instructions (in English)
@@ -13943,10 +13948,10 @@ If сеуште изберете да го исклучите ажурирања
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Master/MASTER.xml b/App/PhotoDemon/Languages/Master/MASTER.xml
index 12b18694a8..4a726313d3 100644
--- a/App/PhotoDemon/Languages/Master/MASTER.xml
+++ b/App/PhotoDemon/Languages/Master/MASTER.xml
@@ -6,7 +6,7 @@
en-US
English (US) - MASTER COPY
- 8.9.1705
+ 9.0.12
Automatically generated from PhotoDemon's source code
Tanner Helland
@@ -2992,6 +2992,11 @@ Description: %2
+
+last line justify
+
+
+
Modify selection
@@ -3014,7 +3019,7 @@ When finished, click the Submit New Issue button. Thank you!
-
+
automatic
@@ -12642,7 +12647,7 @@ Would you like to save your batch list before exiting?
-
+
click here for detailed instructions (in English)
@@ -13888,10 +13893,10 @@ If you still choose to disable updates, don't forget to visit photodemon.org fro
- 2640
+ 2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Master/Phrases.db b/App/PhotoDemon/Languages/Master/Phrases.db
index 0e5f8ae198..2cf70cde06 100644
Binary files a/App/PhotoDemon/Languages/Master/Phrases.db and b/App/PhotoDemon/Languages/Master/Phrases.db differ
diff --git a/App/PhotoDemon/Languages/Polish.xml b/App/PhotoDemon/Languages/Polish.xml
index 26e9f01726..bc7eef2fc5 100644
--- a/App/PhotoDemon/Languages/Polish.xml
+++ b/App/PhotoDemon/Languages/Polish.xml
@@ -6,7 +6,7 @@
pl-PL
Polski
-9.0.0
+9.0.1
100% complete
Ryszard
@@ -3027,6 +3027,11 @@ Opis: %2
Lustro
+
+last line justify
+
+
+
Modify selection
Zmień zaznaczenie
@@ -3051,7 +3056,7 @@ Po zakończeniu kliknij przycisk "Prześlij nowe zgłoszenie".
Instrukcje do raportu o błędzie
-
+
automatic
@@ -12701,7 +12706,7 @@ Dziękujemy!
Kliknij tutaj, aby przekonwertować tę warstwę na tekst zaawansowany.
-
+
click here for detailed instructions (in English)
@@ -13955,10 +13960,10 @@ Jeśli nadal decydujesz się na wyłączenie aktualizacji, nie zapomnij odwiedzi
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Simplified_Chinese.xml b/App/PhotoDemon/Languages/Simplified_Chinese.xml
index bcef6ff144..8f12fa207c 100644
--- a/App/PhotoDemon/Languages/Simplified_Chinese.xml
+++ b/App/PhotoDemon/Languages/Simplified_Chinese.xml
@@ -6,7 +6,7 @@
zh-CN
简体中文
-9.0b1373.7
+9.0b1373.8
完成
ChenLin(QQ:289778005), Lsbdx at 52pojie.cn, shishi
@@ -3023,6 +3023,11 @@ Description: %2
镜像
+
+last line justify
+
+
+
Modify selection
修改选择
@@ -3047,7 +3052,7 @@ When finished, click the Submit New Issue button. Thank you!
错误报告的说明
-
+
automatic
@@ -12695,7 +12700,7 @@ Would you like to save your batch list before exiting?
单击此处将此层转换为高级文本。
-
+
click here for detailed instructions (in English)
@@ -13949,10 +13954,10 @@ If you still choose to disable updates, don't forget to visit photodemon.org fro
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Spanish_(Mexico).xml b/App/PhotoDemon/Languages/Spanish_(Mexico).xml
index ccfaec1a10..d6947d1432 100644
--- a/App/PhotoDemon/Languages/Spanish_(Mexico).xml
+++ b/App/PhotoDemon/Languages/Spanish_(Mexico).xml
@@ -6,7 +6,7 @@
es-MX
español (México)
-9.0.4
+9.0.5
completo
Plinio C Garcia, with help from DeepL.com
@@ -3030,6 +3030,11 @@ Description: %2
reflejando
+
+last line justify
+
+
+
Modify selection
Modificar la selección
@@ -3054,7 +3059,7 @@ Cuando termine, haga clic en el botón "Submit New Issue". Gracias.Instrucciones para informar de fallos
-
+
automatic
@@ -12701,7 +12706,7 @@ Would tarde te gusta guardar su lista de lotes antes de salir?
Haga clic aquí para convertir esta capa en texto avanzado.
-
+
click here for detailed instructions (in English)
@@ -13957,10 +13962,10 @@ If usted todavía elige desactivar las actualizaciones, no se olvide de visitar
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Spanish_(Spain).xml b/App/PhotoDemon/Languages/Spanish_(Spain).xml
index e150a90870..b2aa48f910 100644
--- a/App/PhotoDemon/Languages/Spanish_(Spain).xml
+++ b/App/PhotoDemon/Languages/Spanish_(Spain).xml
@@ -6,7 +6,7 @@
es-ES
español (España)
-6.7.315
+6.7.316
completo
Tecnorama
@@ -3028,6 +3028,11 @@ Descripción: %2
Reflejo
+
+last line justify
+
+
+
Modify selection
@@ -3050,7 +3055,7 @@ When finished, click the Submit New Issue button. Thank you!
Instrucciones para informar de errores
-
+
automatic
@@ -12694,7 +12699,7 @@ Would you like to save your batch list before exiting?
-
+
click here for detailed instructions (in English)
@@ -13950,10 +13955,10 @@ Si, con todo y con ello, elige desactivar las actualizaciones, no olvide visitar
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Traditional_Chinese.xml b/App/PhotoDemon/Languages/Traditional_Chinese.xml
index 594ee7fc38..2fe84af17d 100644
--- a/App/PhotoDemon/Languages/Traditional_Chinese.xml
+++ b/App/PhotoDemon/Languages/Traditional_Chinese.xml
@@ -6,7 +6,7 @@
zh-TW
繁體中文
-7.0.275
+7.0.276
incomplete
Chiahong Hong
@@ -2999,6 +2999,11 @@ Description: %2
鏡像
+
+last line justify
+
+
+
Modify selection
@@ -3021,7 +3026,7 @@ When finished, click the Submit New Issue button. Thank you!
錯誤回報說明
-
+
automatic
@@ -12653,7 +12658,7 @@ Would you like to save your batch list before exiting?
-
+
click here for detailed instructions (in English)
@@ -13901,10 +13906,10 @@ If you still choose to disable updates, don't forget to visit photodemon.org fro
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Turkish.xml b/App/PhotoDemon/Languages/Turkish.xml
index dd39cc2840..6654cf7bc3 100644
--- a/App/PhotoDemon/Languages/Turkish.xml
+++ b/App/PhotoDemon/Languages/Turkish.xml
@@ -6,7 +6,7 @@
tr-TR
Türkçe (Turkish)
-1.0.4
+1.0.5
20% complete
Anıl Yılmaz
@@ -3015,6 +3015,11 @@ Description: %2
+
+last line justify
+
+
+
Modify selection
@@ -3037,7 +3042,7 @@ When finished, click the Submit New Issue button. Thank you!
-
+
automatic
@@ -12667,7 +12672,7 @@ Would you like to save your batch list before exiting?
-
+
click here for detailed instructions (in English)
@@ -13913,10 +13918,10 @@ If you still choose to disable updates, don't forget to visit photodemon.org fro
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/App/PhotoDemon/Languages/Vlaams.xml b/App/PhotoDemon/Languages/Vlaams.xml
index 7a1c24f50e..b2e560b084 100644
--- a/App/PhotoDemon/Languages/Vlaams.xml
+++ b/App/PhotoDemon/Languages/Vlaams.xml
@@ -6,7 +6,7 @@
nl-BE
Vlaams (Nederlands)
-8.9.1701
+8.9.1702
80% complete
Frank Donckers
@@ -3030,6 +3030,11 @@ Beschrijving: %2
spiegeling
+
+last line justify
+
+
+
Modify selection
Selectie wijzigen
@@ -3054,7 +3059,7 @@ Als u klaar bent, klikt u op de knop "Nieuw probleem indienen". Dank U!Bug report instructies
-
+
automatic
@@ -12701,7 +12706,7 @@ Wil je de batchlijst opslaan voordat je afsluit?
Klik hier om deze laag om te zetten in geavanceerde tekst.
-
+
click here for detailed instructions (in English)
@@ -13957,10 +13962,10 @@ If u nog steeds kiezen om updates uit te schakelen, vergeet dan niet om photodem
-2640
+2641
-
-
-
+
+
+
\ No newline at end of file
diff --git a/Classes/pdGlyphCollection.cls b/Classes/pdGlyphCollection.cls
index ef99ebaee3..c2fbddecb3 100644
--- a/Classes/pdGlyphCollection.cls
+++ b/Classes/pdGlyphCollection.cls
@@ -590,7 +590,7 @@ Private Function ParseRawGlyphBuffer(ByRef srcBuffer() As Long, ByRef dstGlyphPa
End Function
-'Prior to calling assembleCompositePath, below, the caller can notify us of custom layout settings via this function
+'Prior to calling AssembleCompositePath, below, the caller can notify us of custom layout settings via this function
Friend Sub NotifyCustomLayoutSettings(Optional ByVal customLineSpacing As Single = 0!, Optional ByVal customCharSpacing As Single = 0!, Optional ByVal customCharOrientation As Single = 0!, Optional ByVal customCharJitterX As Single = 0!, Optional ByVal customCharJitterY As Single = 0!, Optional ByVal customCharInflation As Single = 0!, Optional ByVal customCharMirror As PD_CharacterMirror = cm_None)
m_LineSpacing = customLineSpacing
@@ -609,7 +609,7 @@ End Sub
'After assembling a full glyph collection, this function can be called to generate a totally complete graphics path,
' with all characters laid out according to the passed rect.
-Friend Function AssembleCompositePath(ByRef dstPath As pd2DPath, ByRef boundingRect As RectF, ByVal horizontalAlignment As GP_StringAlignment, ByVal verticalAlignment As GP_StringAlignment, Optional ByVal lineWrapMode As PD_TextWordwrap = tww_AutoWord, Optional ByVal stretchToFit As PD_TextStretchToFit = stf_None) As Boolean
+Friend Function AssembleCompositePath(ByRef dstPath As pd2DPath, ByRef boundingRect As RectF, ByVal horizontalAlignment As GP_StringAlignment, ByVal verticalAlignment As GP_StringAlignment, Optional ByVal lineWrapMode As PD_TextWordwrap = tww_AutoWord, Optional ByVal stretchToFit As PD_TextStretchToFit = stf_None, Optional ByVal justifyLastLine As GP_StringAlignment = StringAlignmentNear) As Boolean
'Initialize the destination path as necessary
If (dstPath Is Nothing) Then Set dstPath = New pd2DPath Else dstPath.ResetPath
@@ -634,7 +634,7 @@ Friend Function AssembleCompositePath(ByRef dstPath As pd2DPath, ByRef boundingR
' pass blank path objects to CalculateGlyphPositions(), and merge the results with our final path when underline
' or strikeout styles are active.
Dim additionalStylesRect As pd2DPath
- CalculateGlyphPositions boundingRect, horizontalAlignment, verticalAlignment, lineWrapMode, additionalStylesRect, stretchToFit
+ CalculateGlyphPositions boundingRect, horizontalAlignment, verticalAlignment, lineWrapMode, additionalStylesRect, stretchToFit, justifyLastLine
Dim i As Long, curGlyph As Long, curGlyphOutline As Long
Dim tmpGlyphOutlineCopy As pd2DPath, tmpMatrix As pd2DTransform, cRandom As pdRandomize
@@ -846,7 +846,7 @@ End Function
'Fill the official xOffset and yOffset parameters of every glyph. Note that all the initial positioning data comes
' from Uniscribe; this function is primarily responsible for line breaks, and any PD-specific positioning changes
' (e.g. modified line or character spacing).
-Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal horizontalAlignment As GP_StringAlignment, ByVal verticalAlignment As GP_StringAlignment, Optional ByVal lineWrapMode As PD_TextWordwrap = tww_AutoWord, Optional ByRef dstStylePath As pd2DPath, Optional ByVal stretchToFit As PD_TextStretchToFit = stf_None) As Boolean
+Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal horizontalAlignment As GP_StringAlignment, ByVal verticalAlignment As GP_StringAlignment, Optional ByVal lineWrapMode As PD_TextWordwrap = tww_AutoWord, Optional ByRef dstStylePath As pd2DPath, Optional ByVal stretchToFit As PD_TextStretchToFit = stf_None, Optional ByVal justifyLastLine As GP_StringAlignment = StringAlignmentNear) As Boolean
Dim i As Long, j As Long
Dim curGlyph As Long, glyphCheck As Long
@@ -1118,6 +1118,7 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
' string is a linebreak, for example.)
If m_Glyphs(curGlyph).isHardLineBreak Then
m_Glyphs(curGlyph).isLastGlyphOnLine = True
+ m_Glyphs(curGlyph).lineID = currentLine - 1 'while we're here, ensure the line ID is correct
'If we are not breaking on a hard line break, we mark the *previous* non-whitespace character
' as the last one on the line.
@@ -1167,17 +1168,21 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
Else
m_Glyphs(0).isLastGlyphOnLine = True
End If
-
+
+ '/end finding+marking the last character of the previous line
End If
- 'Next, we need to mark the first glyph of this line. (This may be required knowledge for subsequent line breaks.)
- ' We do this by advancing the glyph pointer until we encounter a non-whitespace, non-hard-linebreak glyph.
+ 'Next, we need to mark the first glyph of the next line. This mark is important for subsequent
+ ' line handling, particularly justified line alignment.
+
+ 'We do this by advancing the glyph pointer beyond its current point until we encounter a non-whitespace,
+ ' non-hard-linebreak glyph. (This ensures that trailing white space behaves correctly.)
'Note that we must pre-check for the glyph pointer being valid; it may point beyond the end of the array
' if the last character in a string is a hard line-break.
If (curGlyph < m_NumOfGlyphs) Then
- 'If the current glyph is a non-whitespace glyph, mark it as the start of this line.
+ 'If the current glyph is a non-whitespace glyph, mark it as the start of the next line.
If (Not m_Glyphs(curGlyph).isWhiteSpace) Then
m_Glyphs(curGlyph).isFirstGlyphOnLine = True
@@ -1193,11 +1198,16 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
' 2) a hard line-break
Do While incrementGlyphPointer
- 'Exit on non-whitespace glyphs
+ 'The current character is not whitespace (so it's a valid "first of line" character).
If (Not m_Glyphs(curGlyph).isWhiteSpace) Then Exit Do
- 'Exit on hard linebreaks
- If m_Glyphs(curGlyph).isHardLineBreak Then Exit Do
+ 'The current character is a hard linebreak. Advance the glyph pointer to the next glyph
+ ' (whatever it is) because it must be the start of the next line.
+ If m_Glyphs(curGlyph).isHardLineBreak Then
+ curGlyph = curGlyph + 1
+ If (curGlyph <= m_NumOfGlyphs - 1) Then m_Glyphs(curGlyph).isFirstGlyphOnLine = True
+ GoTo SkipToNextGlyph
+ End If
'If we're still here, this is a whitespace glyph. Make sure the line ID is correct
' (treating this glyph as if it belongs to the *previous* line), then increment curGlyph
@@ -1270,7 +1280,8 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
curGlyph = curGlyph + 1
End If
-
+
+SkipToNextGlyph:
Loop While (curGlyph < m_NumOfGlyphs)
'After all glyphs are placed, find the last character in the string, and make sure it's marked as
@@ -1329,29 +1340,32 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
'Start with horizontal alignment
If (horizontalAlignment <> StringAlignmentNear) Then
- 'Justified text requires a lot of special handling, alas
+ 'Justified text requires a lot of special handling, alas...
If (horizontalAlignment = StringAlignmentJustify) Then
'For justified text to work, we need to do a few different things.
' (Note that the current algorithm works on a per-line basis. A more sophisticated algorithm
- ' might rework neighboring lines to "push" a word up or down, or hyphenate words in order to
- ' make the justified text look prettier.)
+ ' might rework neighboring lines to "push" a word up or down - a la the classic Knuth-Plass algorithm -
+ ' or we could hyphenate words to make the justified text look prettier. These more advanced approaches
+ ' are often language-specific which is why I haven't tackled them... yet.)
'Start by iterating lines.
lastGlyphChecked = 0
For curLine = 0 To numOfLines - 1
- 'Skip whitespace-only lines (which were explicitly marked as 0-length in a previous step),
- ' and depending on user settings, also skip the last line in multi-line paragraphs.
+ 'Skip whitespace-only lines (which were explicitly marked as 0-length in a previous step).
+ ' Note that we may also skip the last line in multi-line paragraphs, per the user's
+ ' "last line justify" setting, but we still need to iterate all lines here to look for
+ ' last lines of multi-line paragraphs that are *not* also the last line in the layer.
+ ' (This is possible if a single text layer contains multiple paragraphs.)
Dim justifyThisLine As Boolean
justifyThisLine = (m_LineWidths(curLine) > 0)
- If (numOfLines > 1) Then justifyThisLine = justifyThisLine And (curLine < numOfLines - 1)
+
+ Dim lineEndsInHardBreak As Boolean
+ lineEndsInHardBreak = False
If justifyThisLine Then
- 'Figure out how much space we need to "insert" across the current line to make it justified.
- lineDiff = boundingRectRight - m_LineWidths(curLine)
-
'If possible, we want to insert extra space only where whitespace characters already exist.
' To do that, we need to know how many whitespace characters we have to work with on this line.
Dim numWhiteSpaceChars As Long
@@ -1374,6 +1388,7 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
End If
If m_Glyphs(i).isLastGlyphOnLine Then
+ lineEndsInHardBreak = m_Glyphs(i).isHardLineBreak
idxLastChar = i
If m_Glyphs(i).isWhiteSpace Then m_Glyphs(i).isZeroWidth = True
End If
@@ -1396,28 +1411,38 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
Exit For
End If
- 'Failsafe check only; isLastGlyphOnLine should always terminate correctly, above.
+ 'Blank lines in a row (e.g. LF + LF) can trigger this branch. When this happens,
+ ' we want to manually advance the glyph pointer to the first glyph of the *next* line.
Else
- lastGlyphChecked = i
- Exit For
+
+ If (m_Glyphs(i).lineID > curLine) Then
+ PDDebug.LogAction "WARNING: pdGlyphCollection.CalculateGlyphPositions bad parse."
+
+ 'curLine must be lower than the expected line
+ Else
+
+ 'Manually advance the glyph pointer to the start of the next line
+ lastGlyphChecked = i
+ Do
+ lastGlyphChecked = lastGlyphChecked + 1
+ If (lastGlyphChecked > m_NumOfGlyphs - 1) Then Exit Do
+ Loop While (m_Glyphs(lastGlyphChecked).lineID > curLine)
+
+ 'i is a loop counter and it will be auto-incremented before touching the next glyph,
+ ' so retreat it by 1 so the next iteration starts at "lastGlyphChecked".
+ i = lastGlyphChecked - 1
+
+ End If
+
+ '/end m_Glyphs(i).lineID = curLine
End If
Next i
- 'Before proceeding, we want to forcibly strip whitespace glyphs from the front and end
- ' of each "line". (The way PD's linebreak algorithm works, space characters may arbitrarily
- ' be pushed to the start or end of a line during breaking - this is especially true for
- ' double-spaces, as at the beginning of a sentence.)
- Do While (idxFirstChar < idxLastChar)
- If m_Glyphs(idxFirstChar).isWhiteSpace Then
- m_Glyphs(idxFirstChar).isZeroWidth = True
- idxFirstChar = idxFirstChar + 1
- Else
- Exit Do
- End If
- Loop
-
- Do While (idxLastChar > idxFirstChar)
+ 'Before proceeding, we want to forcibly strip whitespace glyphs from the end of each "line".
+ ' (On a softbreak, whitespace characters are allowed to "drift beyond the margin", but we don't want
+ ' to consider those whitespace chars as valid targets for inserting justifying space.)
+ Do While (idxLastChar > idxFirstChar) And (idxLastChar >= 0)
If m_Glyphs(idxLastChar).isWhiteSpace Then
m_Glyphs(idxLastChar).isZeroWidth = True
idxLastChar = idxLastChar - 1
@@ -1426,12 +1451,58 @@ Private Function CalculateGlyphPositions(ByRef boundingRect As RectF, ByVal hori
End If
Loop
- 'We have now "trimmed" the first and last character indices to point only at the first/last
- ' non-whitespace-chars on this line.
+ 'We have now "trimmed" the last character indices to point only at the last non-whitespace-char
+ ' on this line. (Note that preceding whitespace chars are fine - this allows the user to insert
+ ' e.g. spaces on the first line of a paragraph, and justification will still work correctly.)
'Next, check a couple weird failure states for odd text arrangements.
- If (idxFirstChar >= idxLastChar + 1) Then GoTo NextLineJustify 'Single-character lines
- If (idxFirstChar < 0) Or (idxLastChar < 0) Then GoTo NextLineJustify 'Bad line parsing failsafe
+
+ 'Single-character lines cannot be justified
+ If (idxFirstChar >= idxLastChar + 1) Then GoTo NextLineJustify
+
+ 'Failsafe only for bad line parsing (should never trigger)
+ If (idxFirstChar < 0) Or (idxLastChar < 0) Then GoTo NextLineJustify
+
+ 'Figure out how much space we need to "insert" across the current line to make it justified.
+ lineDiff = boundingRectRight - m_LineWidths(curLine)
+
+ 'Next, look for trailing lines in a paragraph. Trailing lines in a paragraph may need to be
+ ' dealt with specially, depending on the user's current "last line justify" setting.
+ Dim isTrailingLine As Boolean
+ isTrailingLine = False
+
+ 'Trailing lines are either the last line in the paragraph (by default) or any inter-paragraph line
+ ' that terminates in a hard linebreak. (I'm proud that PD handles that second case correctly -
+ ' other photo editors, like GIMP, do not!)
+ If (numOfLines > 1) Then
+ isTrailingLine = (curLine = numOfLines - 1) Or lineEndsInHardBreak
+ End If
+
+ 'Note that we only need to handle trailing lines specially if the user has *not* specified
+ ' last-line justification...
+ If isTrailingLine And (justifyLastLine <> StringAlignmentJustify) Then
+
+ 'The user wants the last line of each paragraph justified left/center/right.
+
+ 'Left-alignment doesn't actually require any work (it's applied by default).
+ If (justifyLastLine <> StringAlignmentNear) Then
+
+ 'Calculate a new per-glyph offset required for the target alignment.
+ If (justifyLastLine = StringAlignmentCenter) Then lineDiff = lineDiff * 0.5!
+
+ 'Apply the offset to each glyph in this line.
+ For j = idxFirstChar To idxLastChar
+ If (Not m_Glyphs(j).isZeroWidth) Then
+ m_Glyphs(j).finalX = m_Glyphs(j).finalX + lineDiff
+ End If
+ Next j
+
+ End If
+
+ 'Continue with the next line (instead of allowing the justify algorithm to continue)
+ GoTo NextLineJustify
+
+ End If
'We now need to split based on the number of whitespace chars in the current line. This works if
' we have at least *one* whitespace char (that is not leading or trailing), but if we have zero,
diff --git a/Classes/pdPSPShape.cls b/Classes/pdPSPShape.cls
index 83614d3fe0..e26aee9634 100644
--- a/Classes/pdPSPShape.cls
+++ b/Classes/pdPSPShape.cls
@@ -500,8 +500,10 @@ Friend Function CreateTextLayerNow(ByRef dstImage As pdImage, ByRef dstLayer As
convertedTextAlignment = StringAlignmentCenter
Case keTextAlignmentRight
convertedTextAlignment = StringAlignmentFar
+
+ 'Failsafe for future PSP changes; assume justified text, I guess?
Case Else
- convertedTextAlignment = StringAlignmentNear
+ convertedTextAlignment = StringAlignmentJustify
End Select
dstImage.GetLayerByID(newLayerID).SetTextLayerProperty ptp_HorizontalAlignment, convertedTextAlignment
diff --git a/Classes/pdTextRenderer.cls b/Classes/pdTextRenderer.cls
index 371326da6d..00af20c876 100644
--- a/Classes/pdTextRenderer.cls
+++ b/Classes/pdTextRenderer.cls
@@ -276,6 +276,7 @@ Private m_StrikeoutSupported As Boolean
' it during rendering stages.
Private m_HorizontalAlignment As GP_StringAlignment
Private m_VerticalAlignment As GP_StringAlignment
+Private m_AlignLastLine As GP_StringAlignment
'Even *more* string settings are not stored in the font itself, or in a StringFormat object, but in the target
' GDI+ Graphics container. These must be assigned to the graphics container prior to painting text, so there's not really
@@ -688,6 +689,7 @@ Friend Function GetAllFontSettingsAsXML() As String
.AddParam "obj-character-jitter-y", m_CharJitterY, True, True
.AddParam "obj-character-inflation", m_CharInflation, True, True
.AddParam "obj-character-mirror", GetTextMirrorStringFromEnum(m_CharMirror), True, True
+ .AddParam "obj-align-last-line", GetAlignmentStringFromUnit(m_AlignLastLine), True, True
End With
'Close out the text object
@@ -757,6 +759,7 @@ Friend Function SetAllFontSettingsFromXML(ByRef srcXMLString As String) As Boole
SetGenericTextProperty ptp_CharJitterY, .GetDouble("obj-character-jitter-y", 0#)
SetGenericTextProperty ptp_CharInflation, .GetDouble("obj-character-inflation", 0#)
SetGenericTextProperty ptp_CharMirror, GetTextMirrorEnumFromString(.GetString("obj-character-mirror", GetTextMirrorStringFromEnum(cm_None), True))
+ SetGenericTextProperty ptp_AlignLastLine, GetAlignmentUnitFromString(.GetString("obj-align-last-line", GetAlignmentStringFromUnit(StringAlignmentNear), True))
End With
'This function does not currently provide a fail state; as long as the load request comes from PD herself, failure should be impossible.
@@ -912,6 +915,9 @@ Friend Function GetGenericTextProperty(ByVal desiredProperty As PD_TextProperty)
Case ptp_CharMirror
GetGenericTextProperty = m_CharMirror
+
+ Case ptp_AlignLastLine
+ GetGenericTextProperty = m_AlignLastLine
End Select
@@ -1179,6 +1185,12 @@ Friend Function SetGenericTextProperty(ByVal desiredProperty As PD_TextProperty,
SetGenericTextProperty = True
End If
+ Case ptp_AlignLastLine
+ If (m_AlignLastLine <> CLng(newValue)) Then
+ m_AlignLastLine = CLng(newValue)
+ SetGenericTextProperty = True
+ End If
+
Case Else
PDDebug.LogAction "WARNING! Unknown text property requested from pdTextRenderer.setGenericTextProperty()"
@@ -1759,7 +1771,7 @@ Private Function RenderTextToDIB_Glyphs(ByRef dstDIB As pdDIB, ByRef srcString A
VBHacks.GetHighResTime startTime
End If
- If m_GlyphCollection.AssembleCompositePath(finalTextPath, boundingRect, m_HorizontalAlignment, m_VerticalAlignment, m_WordWrap, m_StretchToFit) Then
+ If m_GlyphCollection.AssembleCompositePath(finalTextPath, boundingRect, m_HorizontalAlignment, m_VerticalAlignment, m_WordWrap, m_StretchToFit, m_AlignLastLine) Then
If REPORT_TEXT_RENDER_TIMING Then
PDDebug.LogAction "RenderTextToDIB_Glyphs - assembly: " & VBHacks.GetTimeDiffNowAsString(startTime)
@@ -1841,7 +1853,7 @@ Private Function RenderTextToDIB_Glyphs(ByRef dstDIB As pdDIB, ByRef srcString A
End If
Else
- PDDebug.LogAction "WARNING! m_GlyphCollection.assembleCompositePath returned FALSE. Please investigate."
+ PDDebug.LogAction "WARNING! m_GlyphCollection.AssembleCompositePath returned FALSE. Please investigate."
End If
'Release the temporary GDI+ objects we created. (Note that GDI+ objects silently created via pd2D objects are released automatically.)
@@ -2330,6 +2342,7 @@ Private Function SetAllFontSettingsFromXML_Legacy(ByRef srcXMLString As String)
SetGenericTextProperty ptp_CharJitterY, .GetDouble("TextCharJitterY", 0)
SetGenericTextProperty ptp_CharInflation, .GetDouble("TextCharInflation", 0)
SetGenericTextProperty ptp_CharMirror, .GetLong("TextCharMirror", 0)
+ SetGenericTextProperty ptp_AlignLastLine, StringAlignmentNear
End With
'This function does not currently provide a fail state; as long as the load request comes from PD herself, failure should be impossible.
diff --git a/Forms/Toolpanel_Typography.frm b/Forms/Toolpanel_Typography.frm
index 59dc1646c9..bc96b1e6e6 100644
--- a/Forms/Toolpanel_Typography.frm
+++ b/Forms/Toolpanel_Typography.frm
@@ -301,19 +301,19 @@ Begin VB.Form toolpanel_TextAdvanced
End
End
Begin PhotoDemon.pdContainer cntrPopOut
- Height = 1815
+ Height = 2625
Index = 3
- Left = 8400
+ Left = 8520
Top = 3600
Visible = 0 'False
- Width = 6735
- _ExtentX = 11880
- _ExtentY = 3201
+ Width = 6255
+ _ExtentX = 11033
+ _ExtentY = 4630
Begin PhotoDemon.pdSlider sldLineSpacing
Height = 735
Left = 120
TabIndex = 35
- Top = 120
+ Top = 960
Width = 3015
_ExtentX = 5318
_ExtentY = 1296
@@ -325,9 +325,9 @@ Begin VB.Form toolpanel_TextAdvanced
Begin PhotoDemon.pdButtonToolbox cmdFlyoutLock
Height = 390
Index = 3
- Left = 6240
+ Left = 5820
TabIndex = 34
- Top = 1290
+ Top = 2160
Width = 390
_ExtentX = 1111
_ExtentY = 1111
@@ -337,7 +337,7 @@ Begin VB.Form toolpanel_TextAdvanced
Height = 735
Left = 120
TabIndex = 36
- Top = 960
+ Top = 1800
Width = 3015
_ExtentX = 5318
_ExtentY = 1296
@@ -414,6 +414,27 @@ Begin VB.Form toolpanel_TextAdvanced
Min = -1000
Max = 1000
End
+ Begin PhotoDemon.pdButtonStrip btsHAlignJustify
+ Height = 435
+ Left = 150
+ TabIndex = 45
+ Top = 450
+ Width = 1965
+ _ExtentX = 3466
+ _ExtentY = 767
+ ColorScheme = 1
+ End
+ Begin PhotoDemon.pdLabel lblText
+ Height = 240
+ Index = 4
+ Left = 150
+ Top = 120
+ Width = 2940
+ _ExtentX = 5106
+ _ExtentY = 423
+ Caption = "last line justify"
+ ForeColor = 0
+ End
End
Begin PhotoDemon.pdContainer cntrPopOut
Height = 2655
@@ -596,7 +617,7 @@ Begin VB.Form toolpanel_TextAdvanced
End
Begin PhotoDemon.pdButtonStrip btsVAlignment
Height = 435
- Left = 9960
+ Left = 9990
TabIndex = 32
Top = 345
Width = 1455
@@ -610,8 +631,8 @@ Begin VB.Form toolpanel_TextAdvanced
Left = 7920
TabIndex = 33
Top = 0
- Width = 3495
- _ExtentX = 6165
+ Width = 3525
+ _ExtentX = 6218
_ExtentY = 635
Caption = "alignment"
Value = 0 'False
@@ -859,6 +880,43 @@ Private Sub btnFontStyles_SetCustomTabTarget(Index As Integer, ByVal shiftTabWas
End If
End Sub
+Private Sub btsHAlignJustify_Click(ByVal buttonIndex As Long)
+
+ 'If tool changes are not allowed, exit. (Note that this also queries Tools.GetToolBusyState)
+ If (Not Tools.CanvasToolsAllowed) Or (Not CurrentLayerIsText) Then Exit Sub
+
+ 'Mark the tool engine as busy
+ Tools.SetToolBusyState True
+
+ 'Update the current layer text alignment
+ PDImages.GetActiveImage.GetActiveLayer.SetTextLayerProperty ptp_AlignLastLine, buttonIndex
+
+ 'Free the tool engine
+ Tools.SetToolBusyState False
+
+ 'Redraw the viewport
+ Viewport.Stage2_CompositeAllLayers PDImages.GetActiveImage(), FormMain.MainCanvas(0)
+
+End Sub
+
+Private Sub btsHAlignJustify_GotFocusAPI()
+ UpdateFlyout 3, True
+ If (Not PDImages.IsImageActive()) Then Exit Sub
+ Processor.FlagInitialNDFXState_Text ptp_AlignLastLine, btsHAlignJustify.ListIndex, PDImages.GetActiveImage.GetActiveLayerID
+End Sub
+
+Private Sub btsHAlignJustify_LostFocusAPI()
+ Processor.FlagFinalNDFXState_Text ptp_AlignLastLine, btsHAlignJustify.ListIndex
+End Sub
+
+Private Sub btsHAlignJustify_SetCustomTabTarget(ByVal shiftTabWasPressed As Boolean, newTargetHwnd As Long)
+ If shiftTabWasPressed Then
+ newTargetHwnd = Me.btsVAlignment.hWnd
+ Else
+ newTargetHwnd = Me.sldLineSpacing.hWndSlider
+ End If
+End Sub
+
Private Sub btsHAlignment_Click(ByVal buttonIndex As Long)
'If tool changes are not allowed, exit. (Note that this also queries Tools.GetToolBusyState)
@@ -963,7 +1021,7 @@ Private Sub btsVAlignment_SetCustomTabTarget(ByVal shiftTabWasPressed As Boolean
If shiftTabWasPressed Then
newTargetHwnd = Me.btsHAlignment.hWnd
Else
- newTargetHwnd = Me.sldLineSpacing.hWndSlider
+ newTargetHwnd = Me.btsHAlignJustify.hWnd
End If
End Sub
@@ -1413,6 +1471,11 @@ Private Sub Form_Load()
btsVAlignment.AddItem vbNullString, 1
btsVAlignment.AddItem vbNullString, 2
+ btsHAlignJustify.AddItem vbNullString, 0
+ btsHAlignJustify.AddItem vbNullString, 1
+ btsHAlignJustify.AddItem vbNullString, 2
+ btsHAlignJustify.AddItem vbNullString, 3
+
'Fill various character positioning settings
btsStretch.AddItem "none", 0
btsStretch.AddItem "box", 1
@@ -1593,7 +1656,7 @@ End Sub
Private Sub sldLineSpacing_SetCustomTabTarget(ByVal shiftTabWasPressed As Boolean, newTargetHwnd As Long)
If shiftTabWasPressed Then
- newTargetHwnd = Me.btsVAlignment.hWnd
+ newTargetHwnd = Me.btsHAlignJustify.hWnd
Else
newTargetHwnd = Me.cboWordWrap.hWnd
End If
@@ -2068,6 +2131,7 @@ Public Sub SyncSettingsToCurrentLayer()
btnFontStyles(3).Value = CBool(PDImages.GetActiveImage.GetActiveLayer.GetTextLayerProperty(ptp_FontStrikeout))
btsHAlignment.ListIndex = PDImages.GetActiveImage.GetActiveLayer.GetTextLayerProperty(ptp_HorizontalAlignment)
btsVAlignment.ListIndex = PDImages.GetActiveImage.GetActiveLayer.GetTextLayerProperty(ptp_VerticalAlignment)
+ btsHAlignJustify.ListIndex = PDImages.GetActiveImage.GetActiveLayer.GetTextLayerProperty(ptp_AlignLastLine)
cboWordWrap.ListIndex = PDImages.GetActiveImage.GetActiveLayer.GetTextLayerProperty(ptp_WordWrap)
chkFillText.Value = PDImages.GetActiveImage.GetActiveLayer.GetTextLayerProperty(ptp_FillActive)
bsText.Brush = PDImages.GetActiveImage.GetActiveLayer.GetTextLayerProperty(ptp_FillBrush)
@@ -2118,6 +2182,11 @@ Public Sub UpdateAgainstCurrentTheme()
btsVAlignment.AssignImageToItem 1, "format_alignmiddle", , buttonSize, buttonSize, usePDResamplerInstead:=rf_Box
btsVAlignment.AssignImageToItem 2, "format_alignbottom", , buttonSize, buttonSize, usePDResamplerInstead:=rf_Box
+ btsHAlignJustify.AssignImageToItem 0, "format_alignleft", , buttonSize, buttonSize, usePDResamplerInstead:=rf_Box
+ btsHAlignJustify.AssignImageToItem 1, "format_aligncenter", , buttonSize, buttonSize, usePDResamplerInstead:=rf_Box
+ btsHAlignJustify.AssignImageToItem 2, "format_alignright", , buttonSize, buttonSize, usePDResamplerInstead:=rf_Box
+ btsHAlignJustify.AssignImageToItem 3, "format_alignjustify", , buttonSize, buttonSize, usePDResamplerInstead:=rf_Box
+
'Flyout lock controls use the same behavior across all instances
UserControls.ThemeFlyoutControls cmdFlyoutLock
diff --git a/Modules/Processor.bas b/Modules/Processor.bas
index 74da8b8b19..2259854d19 100644
--- a/Modules/Processor.bas
+++ b/Modules/Processor.bas
@@ -925,7 +925,10 @@ Private Function GetNameOfTextAction(ByVal textSettingID As PD_TextProperty) As
Case ptp_CharMirror
GetNameOfTextAction = g_Language.TranslateMessage("mirroring")
-
+
+ Case ptp_AlignLastLine
+ GetNameOfTextAction = g_Language.TranslateMessage("last line justify")
+
Case Else
GetNameOfTextAction = "WARNING! Action name not found!"
diff --git a/Modules/TextTools.bas b/Modules/TextTools.bas
index a6f9ac8956..1bd194595e 100644
--- a/Modules/TextTools.bas
+++ b/Modules/TextTools.bas
@@ -56,6 +56,7 @@ Public Enum PD_TextProperty
ptp_CharInflation = 34
ptp_CharMirror = 35
ptp_StretchToFit = 36
+ ptp_AlignLastLine = 37
End Enum
#If False Then
@@ -65,7 +66,7 @@ End Enum
Const ptp_OutlineActive = 18, ptp_OutlinePen = 19, ptp_BackgroundActive = 20, ptp_BackgroundBrush = 21, ptp_BackBorderActive = 22
Const ptp_BackBorderPen = 23, ptp_LineSpacing = 24, ptp_MarginLeft = 25, ptp_MarginTop = 26, ptp_MarginRight = 27, ptp_MarginBottom = 28
Const ptp_CharRemap = 29, ptp_CharSpacing = 30, ptp_CharOrientation = 31, ptp_CharJitterX = 32, ptp_CharJitterY = 33, ptp_CharInflation = 34
- Const ptp_CharMirror = 35, ptp_StretchToFit = 36
+ Const ptp_CharMirror = 35, ptp_StretchToFit = 36, ptp_AlignLastLine = 37
#End If
'PD's internal glyph renderer supports a number of esoteric capabilities
diff --git a/Modules/Tools.bas b/Modules/Tools.bas
index 33ae3286ea..a6bac798b3 100644
--- a/Modules/Tools.bas
+++ b/Modules/Tools.bas
@@ -889,6 +889,7 @@ Public Sub SyncCurrentLayerToToolOptionsUI()
.SetTextLayerProperty ptp_CharOrientation, toolpanel_TextAdvanced.sltCharOrientation.Value
.SetTextLayerProperty ptp_CharRemap, toolpanel_TextAdvanced.cboCharCase.ListIndex
.SetTextLayerProperty ptp_CharSpacing, toolpanel_TextAdvanced.sltCharSpacing.Value
+ .SetTextLayerProperty ptp_AlignLastLine, toolpanel_TextAdvanced.btsHAlignJustify.ListIndex
End With
'Advanced text layers are rendered using a PhotoDemon-specific renderer.
diff --git a/PhotoDemon.vbp b/PhotoDemon.vbp
index 2d7f5c4c96..d304b7e35c 100644
--- a/PhotoDemon.vbp
+++ b/PhotoDemon.vbp
@@ -513,7 +513,7 @@ Description="PhotoDemon Photo Editor"
CompatibleMode="0"
MajorVer=9
MinorVer=0
-RevisionVer=9
+RevisionVer=12
AutoIncrementVer=1
ServerSupportFiles=0
VersionComments="Copyright 2000-2022 Tanner Helland - photodemon.org"