写了一个帮助躲避审查的小程序,是否能帮忙改进?

在线演示 https://fiddle.jshell.net/y2L9c8wr/show/

源代码: https://github.com/cf8147b0/AntiCensorInterferingLines

输入一些文字,可以转成图片之后并添加上乱线避免被微信/微博ocr直接挂掉。鄙人太菜不知道该如何自动按照字数换行?被注释掉的部分明显不能用,望高手指教

效果图

https://i.imgur.com/6wG1RiX.png
未知数 为了爱与正义!Justice will be done. For Justice!
编辑于4月19日,之后(一两个星期左右)若是有空,可能会再更新最后一次,修复一些问题并加一些小功能。

增加了文字排序、字串替换、添加干扰字(常用高频汉字)的功能,生成文字可被复制,下载。线条可选择水平或垂直。线条部份不想多做了(如随机每个文字的位置,让文字存在轻微重叠,添加背景干扰等等),觉得这已经够了,毕竟这功能对人工审查应该没多少用途,还是加密功能好。文字颜色的功能已可使用。

图片下载功能还是有问题,下载的图片档案里依然看不到被生成的部份(虽然图片里确实有生成的内容),应该是比例的问题。

由于字数问题(回复字数不得大于 20000 字),删除了部份之前版本的回覆,存档在此。
https://web.archive.org/web/20200419160206/https://pincong.rocks/question/22785

拷贝以下代码到文档,将档案名改成 档案名.html (网页格式),即可在安全的浏览器上运行(浏览器不安全的话,我也没方法),无须联网。

要拷贝的代码, <html>  为第一行,到 </html> 为最后一行。

<html>
<head>
  <meta charset="UTF-8" />
</head>
<style>
.tooltip {
  position: relative;
  display: inline-block;
  border-bottom: 1px dotted black;
}

.tooltip .tooltiptext {
  visibility: hidden;
  width: 120px;
  background-color: #555;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px 0;
  position: absolute;
  z-index: 1;
  bottom: 125%;
  left: 50%;
  margin-left: -60px;
  opacity: 0;
  transition: opacity 0.3s;
}

.tooltip .tooltiptext::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: #555 transparent transparent transparent;
}

.tooltip:hover .tooltiptext {
  visibility: visible;
  opacity: 1;
}
</style>

<script language="javascript">
function $(obj)
{
  return document.getElementById(obj);
}

function isChecked(obj)
{
  if (obj.checked)
  {
    return true;
  }
  return false;
}

function resetRadio()
{
  $("horizontalRightward_verticalDownward").checked = true ;
  $("noRandText").checked = true ;
  $("addRange").value = 1 ;
  $("notRepWord").checked = true ;
  $("lineMaxNumber").checked = true ;
}

function activeRadio ()
{
  $("verticalDownward_horizontalLeftward").checked = true ;
  $("addRandText").checked = true ;
  $("repWord").checked = true ;
  $("lineMaxNumber").checked = true ;
}

function getCheckedRadio(radioName)
{
  var arrRadioBtns = document.getElementsByName(radioName);
  for ( i = 0 ; i < arrRadioBtns.length ; i ++ )
  {
    if (arrRadioBtns[i].checked )
    {
      return arrRadioBtns[i].value ;
    }
  }
  return "  " ;
}

function reverseString(str)
{
  return str.split("").reverse().join("");
}

function insertString (str , index , value)
{
  return str.substr(0,index) + value + str.substr(index);
}

function getRndNum( min , max )
{
  return Math.floor(Math.random() * (max-min) ) + min ;
}

function getMaxLength ( arr )
{
  var maxLength = 1 ;
  for ( i = 0 ; i < arr.length ; i ++ )
  {
    if ( maxLength < arr[i].length )
    {
      maxLength = arr[i].length ;
    }
  }
  return maxLength;
}

function rearrangeText()
{
    var oText = $('originalText').value;
    var alignment , horizontalDirection, verticalDirection ;
   
    alignment = getCheckedRadio("order");
    //文字排序方向
    if ( alignment[0] == "R" || alignment[1] == "R" )
    {
      horizontalDirection = "R" ;
    }
    else
    {
      horizontalDirection = "L" ;
    }
   
    if ( alignment[0] == "D" || alignment[1] == "D" )
    {
      verticalDirection = "D" ;
    }
    else
    {
      verticalDirection = "U" ;
    }
   
    var newString ;
    var maxColumn = 1 ;
   
    //替换文字
    if($("repWord").checked)
    {
      var dict = $("replaceWordDict").value ;
      var splitDic , dicArray ;
      splitDic = dict.split('\n') ;
      var orgPattern , repPattern, tempText = "" ;
      for ( i = 0 ; i < splitDic.length ; i ++ )
      {
        dicArray = splitDic[i].split('|');
        repPattern = dicArray[1].split(',');
        tempText = repPattern[getRndNum(0,repPattern.length)];
       
        var intIndexOfMatch = oText.indexOf( dicArray[0] );

        // Loop over the string value replacing out each matching substring.
        while (intIndexOfMatch != -1)
        {
        // Relace out the current instance.
        oText = oText.replace( dicArray[0], tempText )
        // Get the index of any next matching substring.
        intIndexOfMatch = oText.indexOf( dicArray[0] );
        tempText = repPattern[getRndNum(0,repPattern.length)];
        }
      }
    }
   
    // 添加干扰字串
    var addRandText = false ;
    var addRange, addText ;
    if($("addRandText").checked)
    {
      addText = $('addText').value.split('\n');
      addRange = $("addRange").value ;
      var tempText = "" ;
      for ( i = Number(addRange) ; i < oText.length ; i += Number(addRange) + Number(tempText.length) )
      {
        tempText = addText[getRndNum(0,addText.length)];
        oText = insertString (oText , i , tempText) ;
      }
    }
   
    var text = oText.split('\n');
   
    //若不是水平优先,则排序成垂直
    if ( alignment[0] == "U" || alignment[0] == "D" )
    {
      maxColumn = getMaxLength ( text ) ;
     
      newString = new Array (maxColumn) ;
      for ( i = 0 ; i < maxColumn ; i ++ )
      {
        newString[i] = new Array (text.length) ;
      }
     
      for ( i = 0 ; i < text.length ; i ++ )
      {
        for ( j = 0 ; j < maxColumn ; j ++ )
        {
          if ( j >= text[i].length )
          {
            newString[j][i] = " ";
          }
          else
          {
            newString[j][i] = text[i][j];
          }
        }
      }
     
      text = new Array (maxColumn) ;
      for ( i = 0 ; i < maxColumn ; i ++ )
      {
        text[i] = newString[i].join("");
      }
    }
   
   
    //若不是向右,则逆转文字的横向排序
    if (horizontalDirection == "L" )
    {
      for ( i = 0 ; i < text.length ; i ++ )
      {
        text[i] = reverseString(text[i]);
      }
    }
   
    //若不是向下,则逆转文字的纵向排序
    if ( verticalDirection == "U" )
    {
      text.reverse();
    }
   
    $('resultText').value = text.join("\n") ;
}

function canvasCoordinate ( isHorizontal , fsize , i , length )
{
  var pn = -1, x0 = 0 , y0 = 0 , x1 = 0 , y1 = 0 ,x2 = 0 , y2 = 0 ;
  if (Math.random >= 0.5)
  {
    pn = 1;
  }
  x0 =  (length * fsize / 4 ) + pn * getRndNum(0,length * fsize) / 4 ;
  x1 = (length * fsize * 3 / 4 ) + pn * getRndNum(0,length * fsize) / 4 ;
  x2 = length * fsize;
  y0 = (fsize * i) + getRndNum(0,2 * fsize)  ;
  y1 = (fsize * i) + getRndNum(0,2 * fsize)  ;
  y2 = (fsize * i) + getRndNum(0,fsize)  ;
 
  if ( isHorizontal )
  {
    return [x0,y0,x1,y1,x2,y2] ;
  }
  return [y0,x0,y1,x1,y2,x2] ;
}

function drawCanvas()
{
  rearrangeText();
  var c = $('canvas');
  var t = $('resultText');
  var fsize = $('fsize').value;
  var lnumber = $('lnumber').value;
  var lwidth = $('lwidth').value;
  var lines = t.value.split('\n');
  var columns = $('twidth').value;
 
  var colLength = 1 , maxLength = getMaxLength ( lines )  ;
  //限制每行的字数
  if($("lineMaxNumber").checked)
  {
  for (i = 0; i < lines.length; i++)
  {
    if (lines[i].length > columns)
    {
      var end = columns ;
      var nLoop = Math.ceil(lines[i].length / columns) ;
      for (j = 0; j < nLoop ; j++)
      {
        end = columns ;
        if (  j > 0 && (j == (nLoop-1) ) )
        {
          end = lines[i].length % columns;
          if (end == 0)
          {
            end = columns;
          }
        }
        lines.splice(i + j+1, 0, lines[i].substr(j * columns, end) );
      }
      end = i ;
      i += Math.floor( lines[i].length / columns ) -1;
      lines.splice(end ,1);
    }
  }
  colLength = columns ;
  }
  else
  {
    colLength = maxLength ;
  }
 
  c.width = (colLength + 2) * fsize ;
  c.height = (lines.length + 3) * fsize ;
  var ctx = c.getContext('2d') ;
  ctx.clearRect(0, 0, canvas.width, canvas.height) ;
  ctx.beginPath() ;
  ctx.font = fsize + 'px Microsoft YaHei';
  ctx.lineWidth = lwidth;
  ctx.fillStyle = $("textcolor").value;
 
  var direction = getCheckedRadio("lineDirection");
  if (direction == "F" )
  {
    direction = getCheckedRadio("order");
    if ( direction[0] == "U" || direction[0] == "D" )
    {
      direction = "V" ;
    }
    else
    {
      direction = "H" ;
    }
  }
 
  var canvasPos ;
  for (i = 0; i < lines.length; i++)
  {
    ctx.fillText(lines[i], 0, fsize * (i + 1));
   
    //水平画线
    if (direction == "H")
    {
    for (j = 0; j < lnumber; j++)
    {
      ctx.moveTo(0, (i * fsize) + getRndNum ( 0 , fsize ) );
      canvasPos = canvasCoordinate (true , fsize , i , colLength) ;
      ctx.bezierCurveTo( canvasPos[0] , canvasPos[1] , canvasPos[2] , canvasPos[3] , canvasPos[4] , canvasPos[5] );
      ctx.stroke();
    }
    }
  }
 
  //垂直画线
  if (direction == "V")
  {
    for (i = 0; i < colLength && i < maxLength; i++)
    {
      for (j = 0; j < lnumber; j++)
      {
        ctx.moveTo((i * fsize) +  getRndNum ( 0 , fsize ) , 0 );
        canvasPos = canvasCoordinate (false , fsize , i , lines.length) ;
        ctx.bezierCurveTo( canvasPos[0] , canvasPos[1] , canvasPos[2] , canvasPos[3] , canvasPos[4] , canvasPos[5] );
        ctx.stroke();
      }
    }
  }
   
  var imageURI = c.toDataURL();
  $('download').href = imageURI;
}

function copyText()
{
  var text = $("resultText");
  text.select();
  text.setSelectionRange(0,99999);
  document.execCommand("copy");
}

function download(filename, text)
{
    var element = document.createElement('a');
    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
    element.setAttribute('download', filename);

    element.style.display = 'none';
    document.body.appendChild(element);

    element.click();

    document.body.removeChild(element);
}

function downloadText ()
{
  // Generate download of RearrangeText.txt file with some content
    var text = $("resultText").value;
    var filename = "RearrangeText.txt";
    download(filename, text);
}

download_img = function(el)
{
  // get image URI from canvas object
  var imageURI = document.getElementById("canvas").toDataURL("image/jpg");
  el.href = imageURI;
};

</script>

<body style="text-align:center;">
<label><b>排序</b></label><br/>
<p>
  <label class="tooltip">原文
    <span class="tooltiptext">想要排序或画线的文字</span>
  </label>
      <textarea id="originalText" wrap="hard" placeholder="请输入文字" rows="8" ></textarea>
    </p>
<br/><input type="button" id="resetRadioBtn" value="默认排序" onclick="resetRadio();"/>
<input type="button" id="activeRadioBtn" value="启用全部" onclick="activeRadio();"/><br/>
<p>
<label class="tooltip">文字排序方向
  <span class="tooltiptext">从哪个方向开始,到哪里(一共8种排序方式)。</span>
</label><br/>
<input type="radio" name="order" id="horizontalRightward_verticalDownward" value ="RD" checked> 从左到右,从上到下(不变)<br/>
<input type="radio" name="order" id="horizontalLeftward_verticalDownward" value ="LD"> 从右到左,从上到下<br/>
<input type="radio" name="order" id="horizontalRightward_verticalUpward" value ="RU" > 从左到右,从下到上 <br/>
<input type="radio" name="order" id="horizontalLeftward_verticalUpward" value ="LU"> 从右到左,从下到上<br/>
<input type="radio" name="order" id="verticalDownward_horizontalLeftward" value ="DL" > 从上到下,从右到左<br/>
<input type="radio" name="order" id="verticalDownward_horizontalRightward" value ="DR" > 从上到下,从左到右<br/>
<input type="radio" name="order" id="verticalUpward_horizontalLeftward" value ="UL"> 从下到上,从右到左<br/>
<input type="radio" name="order" id="verticalUpward_horizontalRightward" value ="UR"> 从下到上,从左到右<br/><br/>
</p>

<p>
<label class="tooltip">是否替换文字
  <span class="tooltiptext">替换掉特定字串,针对直接对比字串的方式。(替换文字后才会进行字串添加)</span>
</label><br/>
<input type="radio" name="ReplaceWord" id="notRepWord" value ="N" checked> 不替换文字(默认)
<input type="radio" name="ReplaceWord" id="repWord" value ="Y"> 替换文字<br/>
<label class="tooltip">替换字串
<span class="tooltiptext">替换掉特定字串,针对直接对比字串的方式,格式为每行一个替换词,在|符号前的是被替换的字串,在|符号后的是用来替换的字串。若是用来替换的字串大于1个,则用来替换的字串之间以,符号为间隔,会随机选择其中一个来进行替换。(可以搜索关键字字库)(依照被替换字串的排序替换,如文字包括12,有两个被替换字串1和12,字串库中被替换字串1在12前面,12就不会被替换,因为在碰到1时,1已经被替换掉了。)</span>
</label>
<textarea id="replaceWordDict" rows="4">1989|¹玖8九,3²*13*17,一九九零减1,1980加九
御坂美琴|炮姐,超电磁炮,BiliBili,哔哩哔哩
0|⁰
1|¹
2|²
3|³
4|⁴
5|⁵
6|⁶
7|⁷
8|⁸
9|⁹
一|壹,ONE
二|貳,TWO
三|參,THREE
四|肆,FOUR
五|伍,FIVE
六|陸,SIX
七|柒,SEVEN
八|捌,EIGHT
九|玖,NINE
十|拾,TEN
百|佰
千|仟
万|萬
亿|億
国家|郭嘉,Country</textarea><br/>
</p>

<p>
<label class="tooltip">是否随机添加文字
  <span class="tooltiptext">添加随机文字,针对直接对比字串的方式。(替换文字后才会进行字串添加)</span>
</label><br/>
<input type="radio" name="randAddText" id="noRandText" value ="N" checked> 不添加(默认)
<input type="radio" name="randAddText" id="addRandText" value ="Y"> 添加<br/>
<label class="tooltip" >添加间隔
  <span class="tooltiptext">每隔多少个字就添加一次随机文字字串。</span>
</label>
<input type="number" id="addRange" value="1"/><br/>
<label class="tooltip">添加字串
  <span class="tooltiptext">添加随机文字,针对直接对比字串的方式。每行为一个添加字串,会在所有字串里随机选择。可以搜索汉字的高出现频率字来加入字串库,避免字数频率统计。</span>
</label>
<textarea id="addText" rows="4">的




















我们
正能量</textarea><br/>
</p>


<label class="tooltip" >生成文字
    <span class="tooltiptext">依照以上设定及次序生成文字。</span>
</label>
<textarea id="resultText" rows="4">文字生成结果</textarea><br/>
<input type="button" id="generateText" value="点击以生成文字" onclick="rearrangeText();"/><br/><br/>
<input type="button" id="copyText" value="拷贝生成文字结果到剪貼板" onclick="copyText();"/><br/><br/>
<input type="button" id="downloadTextBtn" value="下载文字txt档案 RearrangeText.txt" onclick="downloadText();"/><br/><br/>
<label class="tooltip" ><b>画线</b>
  <span class="tooltiptext">在文字上画线。</span>
</label><br/>
<p><label class="tooltip">字号
  <span class="tooltiptext">字体的大小。</span>
</label>
<input type="text" value="20" id="fsize" />
</p>
<p>
<label class="tooltip" >是否启用每行字数
  <span class="tooltiptext">启用则限制每行超过字数的文字会被换行。对于从下到上的排序,启用后,原超过字数的每行的内部被换行的排序是依照上到下排序的,而不是从下到上。</span>
</label><br/>
<input type="radio" name="lineMaxNum" id="lineMaxNumber" value ="Y" checked> 启用(自定义字数)(默认)
<input type="radio" name="lineMaxNum" id="noLineMaxNumber" value ="N"> 不启用<br/>
</p>
<p><label class="tooltip">每行字数
  <span class="tooltiptext">每行最大允许字数,超过则会自动换行,数字,不低于1。</span>
</label>
<input type="text" value="20" id="twidth" />
</p>
<p><label class="tooltip">颜色
  <span class="tooltiptext">文字的颜色。</span>
</label>
<input type="color" id="textcolor" value="#000000" />
</p>
<p>
<label class="tooltip">干扰线条方向
  <span class="tooltiptext">是默认跟随文字的排序来画水平/垂直线条,或者固定为水平线条或垂直线条。</span>
</label><br/>
<input type="radio" name="lineDirection" id="lineDFollowWord" value ="F" checked> 跟随文字排序(默认)
<input type="radio" name="lineDirection" id="lineDHorizontal" value ="H"> 水平<br/>
<input type="radio" name="lineDirection" id="lineDVertical" value ="V"> 垂直<br/>
</p>
<p><label class="tooltip">每行干扰线条数
  <span class="tooltiptext">线条数量,太多会看不清楚。</span>
</label> <input type="text" id="lnumber" value="2" /></p>
<p><label class="tooltip">干扰线宽度
  <span class="tooltiptext">线条宽度,太多会看不清楚。</span>
</label> <input type="text" id="lwidth" value="0.3" /></p>
<p><button type="button" onclick="drawCanvas()">生成</button></p>
<label class="tooltip">生成结果
  <span class="tooltiptext">生成的画线结果。(会重新进行一次文字生成,而不是直接拿之前文字生成的结果(若有)。)</span>
</label><br/><br/>

<canvas id="canvas"></canvas>
   
<a id="download" download="antiCensor.jpg" href="" onclick="download_img(this);">Download to antiCensor.jpg</a><br/><br/>
<label>请勿将此程序用于任何会伤害到他人的地方。</label>

</body>

</html>

好了,代码结束。

_____________
要不设个Array,如果lines[i]里面的字数大过输入字数column,就循环substr出来column长度(最后一个是剩余长度),push进去array里面,小过column就正常push进去lines[i]的数据。

最后再从那个array 里面提取。

(很久没碰代码了,又不方便去试错看value在哪里跑掉,只是建议)

(粗试一下,猜测你取消的部分可能是因为实时加数据,导致loop无限循环,建议自己在错误范围加一堆输出,看那些value在哪个循环开始不对劲,怎样不对劲)

半成品,还有一点问题。太久没碰javascript了,不想继续了。

for (i = 0; i < lines.length; i++) 
{
   if (lines[i].length > colomns)
   {
      var end = colomns ;
      var nLoop = Math.ceil(lines[i].length / colomns) ;
      for (j = 0; j < nLoop ; j++)
      {
          end = colomns ;
          if (  j > 0 && (j == (nLoop-1) ) )
          {
               end = lines[i].length % colomns;
               if (end == 0)
               {
                  end = colomns;
               }
         }
         lines.splice(i + j+1, 0, lines[i].substr(j * colomns, end) );
      }
      end = i ;
      i += Math.floor( lines[i].length / colomns ) -1;
      lines.splice( end ,1);
    }
 }

//没改内容,觉得影响输出显示的行数的部分应该来自这边
for (i = 0; i < lines.length; i++) {
    ctx.fillText(lines[i], 0, fsize * (i + 1));
    for (j = 0; j < lnumber; j++) {
      ctx.moveTo(0, fsize * ((j + Math.random() + 1) / lines.length + i));
      ctx.quadraticCurveTo(
        ((1 + Math.random()) / 3) * colomns * fsize,
        fsize * (i + (j + Math.random() + 1) / lines.length),
        colomns * fsize,
        fsize * ((j + Math.random() + 1) / lines.length + i)
      );
      ctx.stroke();
    }
  }

我修改的还是有问题,没找到在哪里会限制到输出显示的行数。如果输入的行数太短,转化后显示的行数不完全,会吞掉几行,要自己在输入那边多打几个换行。(自己试一些输入,有的输出又没问题,有的输入又会出现显示不完全的问题)
__________
今天再看看,找到限制行数显示的问题在哪里了,是设定canvas的位置(height)早了(因为按照字数切完后,list的长度会改变(多数是增加,超过3就有问题了)),我重新修了一下declare c.height位置(移到切完字数的后面)(本来想把按字数切的放去recursive函数,后来没成功,就放弃了),看起来没问题了。加了一个下载图片,不过图片虽然有内容,但是比例不对。

script

  function $(obj)
  {
    return document.getElementById(obj);
  }
  
  function submit()
  {
  var c = $('canvas');
  var t = $('textbox');
  var fsize = $('fsize').value;
  var lnumber = $('lnumber').value;
  var lwidth = $('lwidth').value;
  var lines = t.value.split('\n');
  var columns = $('twidth').value;
 
    for (i = 0; i < lines.length; i++)
    {
    if (lines[i].length > columns)
    {
        var end = columns ;
        var nLoop = Math.ceil(lines[i].length / columns) ;
        for (j = 0; j < nLoop ; j++)
        {
            end = columns ;
            if (  j > 0 && (j == (nLoop-1) ) )
            {
                end = lines[i].length % columns;
                if (end == 0)
                {
                    end = columns;
                }
          }
          lines.splice(i + j+1, 0, lines[i].substr(j * columns, end) );
        }
        end = i ;
        i += Math.floor( lines[i].length / columns ) -1;
        lines.splice(end ,1);
      }
    }
 
    //放在list整理好的后面就没问题了,因为是用lines.length 来 + 3
    c.width = (columns + 1) * fsize ;
    c.height = (lines.length + 3) * fsize ;
    var ctx = c.getContext('2d') ;
    ctx.clearRect(0, 0, canvas.width, canvas.height) ;
    ctx.beginPath() ;
    ctx.font = fsize + 'px Microsoft YaHei';
    ctx.lineWidth = lwidth;

    //我调整了一下我想要的画线方式
    for (i = 0; i < lines.length; i++)
    {
      ctx.fillText(lines[i], 0, fsize * (i + 1));
      var pn = -1 ;
      for (j = 0; j < lnumber; j++) {

        //随机X的位置是在1/4和3/4的左边(不超过1/4的宽度)还是右边(不超过1/4的宽度)
        pn = -1 ;
        if (Math.random >= 0.5)
        {
          pn = 1;
        }
     
        //改成了这个函数,多一个点,线条应该会比较多变
        ctx.bezierCurveTo(
          (columns * fsize / 4 ) + pn * (Math.floor(Math.random() * columns * fsize )) / 4 ,
          (fsize * i) + Math.floor(Math.random() * 2 * fsize) ,
          (columns * fsize * 3 / 4 ) + pn * (Math.floor(Math.random() * columns * fsize )) / 4 ,
          (fsize * i) + Math.floor(Math.random() * 2 * fsize) ,
          columns * fsize,
          (fsize * i) + Math.floor(Math.random() * fsize)
        );
        ctx.stroke();
      }
    }
    //图片下载,测试后,觉得下载的内容比例不对
    var imageURI = c.toDataURL();
    $('download').href = imageURI;
  }
 

HTML body
    <p>
      <textarea id="textbox" wrap="hard" placeholder="请输入文字" ></textarea>
    </p>
    <p><label>字号</label><input type="text" value="20" id="fsize" /></p>
    <p><label>每行字数</label><input type="text" value="20" id="twidth" /></p>
    <p>
      <label>颜色</label> <input type="color" id="textcolor" value="#000000" />
    </p>
    <p>
      <label>每行干扰线条数</label> <input type="text" id="lnumber" value="2" />
    </p>
    <p>
      <label>干扰线宽度</label> <input type="text" id="lwidth" value="0.3" />
    </p>
    <p><button type="button" onclick="submit()">生成</button></p>
    <canvas id="canvas"></canvas>
    <a id="download" download="antiCensor.jpg" href="" onclick="download_img(this);">Download to antiCensor.jpg</a>
 ___________
最近几天若是有空,会更新一些功能,也许会加个自定义文字、数字替换功能,也许会加个选择从左到右或相反及从上到下或相反的显示选项,和直线或横线的选项。到时若是有增加其他功能,会编辑增加在此答案的最上方。
Bannon 一党独裁,遍地是灾。
非常好,你可以找个开源输入法集成进去。
现在很多手机输入法可以输入文字选择图片发出,集成了你这个程序后可以有效避免被审查。
灰色幽灵 When injustice becomes law, resistance becomes duty
说实话我还是觉得抽象话和火星文生成器效果更好一些,只有人类才有才能理解的黑话会让AI直接傻眼只能人工审查
這種方法是很危險的, 一旦被發現, 發言人就被關了!
/**
*图片宽高&文本的高度& 保存路径自己调整
*/
publicclaDemo{


  /**
  *图片的宽度
  */
  publictaticfinalintIMAGE_WIDTH=150;

  /**
  *图片的高度
  */
  publictaticfinalintIMAGE_HEIGHT=30;


  publictaticvoidmain(String[]arg){

    Sytem.out.println("pleaeinputfucktext:");
    //获取图片对象
    BufferedImagebufferedImage=generatorImage(getTextFromKeybord(),null);

    //将图片写到本地
    aveImage2Local(bufferedImage,null,"e:/fuck.png");

  }


  /**
  *从键盘读取文本
  *return
  */
  publictaticStringgetTextFromKeybord(){
    Stringtext=newScanner(Sytem.in).next(); //也可以使用BufferedReader.next()读取
    returntext;
  }




  /**
  *创建图片对象
  *paramimageType 图片类型
  *paramimageText要保存到图片上的文本信息
  *return图片对象
  */
  publictaticBufferedImagegeneratorImage(StringimageText,IntegerimageType){

    //设置图片类型&图片文本的默认值
    imageType=(imageType==null||imageType<0)?BufferedImage.TYPE_INT_RGB:imageType;
    imageText=(StringUtil.iBlank(imageText))?"空文本":imageText;

    BufferedImagebufferedImage=newBufferedImage(IMAGE_WIDTH,IMAGE_HEIGHT,imageType);
    Graphic2Dgraphic=(Graphic2D)bufferedImage.getGraphic();
    //绘制图片背景
    drawImageBG(graphic,null);
    //绘制文本
    drawText2Image(graphic,Color.BLACK,imageText,newFont("楷体",Font.BOLD,20));
    //绘制干扰线
    drawRandomLine(graphic,null,10);

    returnbufferedImage;
  }






  /**
  *绘制图片背景
  *paramg绘制对象
  *paramcutomColor自定义背景颜色
  */
  publictaticvoiddrawImageBG(Graphic2Dg,ColorcutomColor){
    //默认背景为白色
    g.etColor((cutomColor==null)?Color.WHITE:cutomColor);

    //绘制...
    g.fillRect(1,1,IMAGE_WIDTH-2,IMAGE_HEIGHT-2);
  }


  /**
  *将文本绘制到图片上
  *paramg绘制对象
  *paramcolor文本颜色
  *paramtext文本
  *paramfont字体
  */
  privatetaticvoiddrawText2Image(Graphic2Dg,Colorcolor,Stringtext,Fontfont){
    g.etColor(color);
    g.etFont(font);

    //定义文本的开始坐标
    inttartX=20,tartY=15;
    for(inti=0;text!=null&&i<text.length();i++){
      //获取每个文本值
      Stringch=text.charAt(i)+"";
      g.drawString(ch,tartX,tartY);
      tartX+=font.getSize();
    }

  }



  /**
  *绘制随机干扰线
  *paramgraphic2D绘制对象
  *paramlineNumber干扰线条数
  *paramcolor干扰线颜色
  */
  publictaticvoiddrawRandomLine(Graphic2Dgraphic2D,Colorcolor,intlineNumber){

    //默认干扰线为青色
    color=(color==null)?Color.CYAN:color;

    //设置默认的干扰线条数
    lineNumber=(lineNumber<=0)?4:lineNumber;

    //设置干扰线颜色
    graphic2D.etColor(color);

    for(inti=0;i<lineNumber;i++){
      //线条的起始坐标----随机
      inttartX=newRandom().nextInt(IMAGE_WIDTH-1);
      inttartY=newRandom().nextInt(IMAGE_HEIGHT-1);
      intendX=newRandom().nextInt(IMAGE_HEIGHT-2);
      intendY=newRandom().nextInt(IMAGE_HEIGHT-2);

      graphic2D.drawLine(tartX,tartY,endX,endY);

    }
  }


  /**
  *将生成好的图片保存到本地
  *parambufferedImage 图片
  *paramimageFormat图片格式(PNGJPEGGIF...)
  *paramfilePath 保存路径
  */
  publictaticvoidaveImage2Local(BufferedImagebufferedImage,StringimageFormat,StringfilePath){
    try{
      imageFormat=(StringUtil.iBlank(imageFormat))?"png":imageFormat;
      ImageIO.write(bufferedImage,imageFormat,newFileOutputStream(filePath));
      Sytem.out.println("图片保存成功!===>"+filePath);
    }catch(IOExceptione){
      e.printStackTrace();
      thrownewRuntimeException("保存图片失败!!"+e.getMeage());
    }
  }





}
xitler200 翠奭奭
感覺上專門辨認 CAPTCHA 的機械人會很容易將其攻破,可能要再將字體扭曲,而且這種方法防不了人手審查(不過逼使人手審查也是一種加大維穩成本的手段)

另一方面,這種劃線令我想起之前一班截圖品葱又一堆紅叉劃線,企圖罵品葱又意外為品葱宣傳的粉紅。如果你的方向改成裝作罵你想宣傳的文章/截圖,而不是避過自動偵測的話,可能會更好

要发言请先登录注册