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"